30.15 開機自啓動的功能設計
30.15.1 普通模式
對於Android7.0之前的系統,如果設計APP在系統啓動之後自啓動,只需在APP添加處理ACTION_BOOT_COMPLETED廣播消息的代碼即可。
30.15.2直接啓動模式
從Android7.0開始,系統有了一種新的啓動模式“直接啓動”模式,針對這種模式需要做特別處理。
當設備已開機但用戶尚未解鎖設備時,Android N 將在安全的“直接啓動”模式下運行。 爲支持此操作,系統爲數據提供兩個存儲位置:
憑據加密存儲,這是默認存儲位置,僅在用戶解鎖設備後可用。
設備加密存儲,該存儲位置在“直接啓動”模式下和用戶解鎖設備後均可使用。
默認情況下,應用不會在“直接啓動”模式下運行。如果應用需要在“直接啓動”模式下進行操作,可以註冊在此模式期間應運行的應用組件。 對於需要在“直接啓動”模式下運行的應用,常見的一些用例包括:
已安排通知的應用,如鬧鐘應用。
提供重要用戶通知的應用,如短信應用。
提供無障礙服務的應用,如 Talkback。
如果應用在“直接啓動”模式下運行時需要訪問數據,則使用設備加密存儲。 設備加密存儲包含使用密鑰加密的數據,僅在設備已成功執行驗證啓動後密鑰纔可用。
對於應使用與用戶憑據(如 PIN 或密碼)關聯的密鑰加密的數據,請使用憑據加密存儲。憑據加密存儲僅在用戶已成功解鎖設備後可用,直到用戶再次重啓設備。 如果用戶在解鎖設備後啓用鎖定屏幕,則不會鎖定憑據加密存儲。
30.15.3 請求在直接啓動時運行
應用必須先向系統註冊其組件,然後才能在“直接啓動”模式下運行或訪問設備加密存儲。應用通過將組件標記爲“加密感知”來註冊系統。 若要將組件標記爲加密感知,需在清單中將 android:directBootAware 屬性設爲 true。
當設備重啓後,加密感知組件可以註冊接收來自系統的 LOCKED_BOOT_COMPLETED 廣播消息。 此時設備加密存儲可用,APP組件可以在“直接啓動”模式下執行需要運行的任務,例如觸發已設定的鬧鈴。
以下代碼段展示如何將 BroadcastReceiver 註冊爲加密感知並在應用清單中爲 LOCKED_BOOT_COMPLETED 添加 Intent 過濾器:
<receiver
android:directBootAware="true" >
...
<intent-filter>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
</intent-filter>
</receiver>
在用戶解鎖設備後,所有組件均可訪問設備加密存儲以及憑據加密存儲。
30.15.4 訪問設備加密存儲
要訪問設備加密存儲,需通過調用 Context.createDeviceProtectedStorageContext() 創建另一個 Context 實例。使用此上下文執行的所有存儲 API 調用均訪問設備加密存儲。 以下示例訪問設備加密存儲並打開現有應用數據文件:
Context directBootContext = appContext.createDeviceProtectedStorageContext();
// Access appDataFilename that lives in device encrypted storage
FileInputStream inStream = directBootContext.openFileInput(appDataFilename);
// Use inStream to read content...
僅針對在“直接啓動”模式下必須可訪問的信息使用設備加密存儲。請勿將設備加密存儲用作通用加密存儲。對於專有用戶信息,或在“直接啓動”模式下不需要的加密數據,請使用憑據加密存儲。
30.15.5 接收用戶解鎖通知
重啓後一旦用戶解鎖了設備,應用即可切換至訪問憑據加密存儲,並使用依賴用戶憑據的常規系統服務。
爲了在重啓後用戶解鎖設備時收到通知,請從一個正在運行的組件註冊 BroadcastReceiver 以偵聽 ACTION_USER_UNLOCKED 消息。 或者,可以接收現有 ACTION_BOOT_COMPLETED 消息,該消息現在表明設備已啓動,且用戶已解鎖設備。
可以通過調用 UserManager.isUserUnlocked() 直接查詢用戶是否已解鎖設備。
30.15.6 示例代碼
在AndroidManifest.xml文件中註冊廣播接收器:
<receiver android:name=".BootBroadcastReceiver"
android:directBootAware="true">
<!-- Listening the BOOT_COMPLETEDaction for legacy pre-N devices -->
<intent-filter>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
廣播接收器代碼:
public class BootBroadcastReceiver
extends BroadcastReceiver {
private static final String
TAG= "BootBroadcastReceiver";
@Override
public void onReceive(Contextcontext, Intent intent) {
boolean bootCompleted;
String action =intent.getAction();
Log.i(TAG,
"Receivedaction: " + action +
", user unlocked: " + UserManagerCompat
.isUserUnlocked(context));
if (BuildCompat.isAtLeastN()){
bootCompleted= Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action);
} else {
bootCompleted= Intent.ACTION_BOOT_COMPLETED.equals(action);
}
}
}