本開發人員指南將說明裝置政策控制器 (DPC) 如何 在專用裝置上管理多位 Android 使用者。
總覽
DPC 可以協助多人共用一部專用裝置。您的裝置政策控制器 (DPC) 在全代管裝置上執行的應用程式可以建立及管理兩種使用者:
- 次要使用者是 Android 使用者,且已儲存獨立的應用程式和資料 。您將透過管理員元件管理使用者。這些使用者 適用於於轉換開始時取貨的情況,例如 外送司機或資安工作者。
- 暫時使用者是次要使用者,系統會在使用者進行操作時將其刪除 停止、切換或裝置重新啟動。這類使用者 可在工作階段結束後刪除資料,例如公開存取 網際網路資訊站
透過現有的 DPC 管理專用裝置和次要裝置 使用者。DPC 中的管理員元件會將自己設為新次要次要執行個體的管理員 建立使用者
次要使用者的管理員必須與 全代管裝置。如要簡化開發作業,建議您與管理員聯絡 讓裝置與次要使用者之間保持適當平衡
如要在專用裝置上管理多位使用者,通常需要 Android 9.0、 然而,這份開發人員指南中使用的部分方法已列於 也就是較舊版本的 Android 系統
次要使用者
次要使用者可以連線至 Wi-Fi 並設定新網路。但 也無法編輯或刪除網路,包括已建立的網路。
建立使用者
DPC 可以在背景建立其他使用者,而且使用者可以切換 移到前景次要和次要執行個體的程序大致相同 以及臨時使用者請在完全採用 Google Cloud 控制台的 受管理的裝置和次要使用者:
- 呼叫
DevicePolicyManager.createAndManageUser()
。 如要建立臨時使用者,請加入MAKE_USER_EPHEMERAL
。 - 致電
DevicePolicyManager.startUserInBackground()
到 並在背景啟動使用者使用者已開始運轉,但您也希望 完成設定,然後再將使用者導向前景 操作者。 - 在次要使用者的管理員中,呼叫
DevicePolicyManager.setAffiliationIds()
到 讓新使用者與主要使用者建立聯盟關係。詳情請見 DPC 協調。 - 返回全代管裝置的管理員,發起通話
DevicePolicyManager.switchUser()
:將使用者切換為 前景
以下範例說明如何在 DPC 中新增步驟 1:
Kotlin
val dpm = getContext().getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager // If possible, reuse an existing affiliation ID across the // primary user and (later) the ephemeral user. val identifiers = dpm.getAffiliationIds(adminName) if (identifiers.isEmpty()) { identifiers.add(UUID.randomUUID().toString()) dpm.setAffiliationIds(adminName, identifiers) } // Pass an affiliation ID to the ephemeral user in the admin extras. val adminExtras = PersistableBundle() adminExtras.putString(AFFILIATION_ID_KEY, identifiers.first()) // Include any other config for the new user here ... // Create the ephemeral user, using this component as the admin. try { val ephemeralUser = dpm.createAndManageUser( adminName, "tmp_user", adminName, adminExtras, DevicePolicyManager.MAKE_USER_EPHEMERAL or DevicePolicyManager.SKIP_SETUP_WIZARD) } catch (e: UserManager.UserOperationException) { if (e.userOperationResult == UserManager.USER_OPERATION_ERROR_MAX_USERS) { // Find a way to free up users... } }
Java
DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); // If possible, reuse an existing affiliation ID across the // primary user and (later) the ephemeral user. Set<String> identifiers = dpm.getAffiliationIds(adminName); if (identifiers.isEmpty()) { identifiers.add(UUID.randomUUID().toString()); dpm.setAffiliationIds(adminName, identifiers); } // Pass an affiliation ID to the ephemeral user in the admin extras. PersistableBundle adminExtras = new PersistableBundle(); adminExtras.putString(AFFILIATION_ID_KEY, identifiers.iterator().next()); // Include any other config for the new user here ... // Create the ephemeral user, using this component as the admin. try { UserHandle ephemeralUser = dpm.createAndManageUser( adminName, "tmp_user", adminName, adminExtras, DevicePolicyManager.MAKE_USER_EPHEMERAL | DevicePolicyManager.SKIP_SETUP_WIZARD); } catch (UserManager.UserOperationException e) { if (e.getUserOperationResult() == UserManager.USER_OPERATION_ERROR_MAX_USERS) { // Find a way to free up users... } }
建立或建立新使用者時,您可以查看失敗的原因
擷取 UserOperationException
例外狀況並呼叫
getUserOperationResult()
。超出使用者數量
導致失敗的常見原因包括:
建立使用者可能需要一段時間。如果你經常建立使用者 引導使用者立即在背景執行,提升使用者體驗。 您可能會需要在可開始使用的使用者之優勢和最高 單一裝置允許的使用者人數。
身分認同
建立新使用者後,請使用永久序列參照使用者
號碼。不要保留 UserHandle
,因為系統會在您回收這些物件時進行回收
建立及刪除使用者透過撥打電話取得序號
UserManager.getSerialNumberForUser()
:
Kotlin
// After calling createAndManageUser() use a device-unique serial number // (that isn’t recycled) to identify the new user. secondaryUser?.let { val userManager = getContext().getSystemService(UserManager::class.java) val ephemeralUserId = userManager!!.getSerialNumberForUser(it) // Save the serial number to storage ... }
Java
// After calling createAndManageUser() use a device-unique serial number // (that isn’t recycled) to identify the new user. if (secondaryUser != null) { UserManager userManager = getContext().getSystemService(UserManager.class); long ephemeralUserId = userManager.getSerialNumberForUser(secondaryUser); // Save the serial number to storage ... }
使用者設定
您可以根據使用者需求,自訂
使用者。您可以在呼叫 createAndManageUser()
時加入下列旗標:
SKIP_SETUP_WIZARD
- 略過執行新的使用者設定精靈,用於檢查及安裝更新, 提示使用者新增 Google 帳戶與 Google 服務, 就會啟動螢幕鎖定功能這項作業可能需要一些時間才能完成,而且不一定適用於所有使用者 使用者的資料,例如公開網際網路資訊站。
LEAVE_ALL_SYSTEM_APPS_ENABLED
- 讓新使用者啟用所有系統應用程式。如未設定這個標記 新使用者僅包含手機所需的最少應用程式 通常是檔案瀏覽器、電話撥號程式、聯絡人和簡訊。
追蹤使用者生命週期
DPC (如果是全代管裝置的管理員) 可能會想
也能得知次要使用者的變更時間如要在變更後執行後續工作,請覆寫
您 DPC DeviceAdminReceiver
子類別中的下列回呼方法:
onUserStarted()
- 系統啟動使用者後呼叫。這位使用者可能仍在設定,或
會在背景執行您可以從
startedUser
取得使用者 引數 onUserSwitched()
- 系統切換至其他使用者後呼叫。您也能獲得新使用者
目前是透過
switchedUser
引數在前景執行。 onUserStopped()
- 系統因使用者登出而停止使用者後呼叫,改為
(如果是臨時使用者),或 DPC 停止該名使用者。您可以
透過
stoppedUser
引數傳回使用者。 onUserAdded()
- 在新增使用者時呼叫。次要使用者
DPC 收到回呼時完整設定。您可以從
newUser
引數。 onUserRemoved()
- 系統刪除使用者後呼叫。由於使用者已遭刪除
您無法存取
removedUser
引數代表的使用者。
得知系統何時將使用者導向前景或將使用者導向至前景
應用程式便可註冊接收端
ACTION_USER_FOREGROUND
和
ACTION_USER_BACKGROUND
廣播。
探索使用者
如要取得所有次要使用者,全代管裝置管理員可以呼叫
DevicePolicyManager.getSecondaryUsers()
。成果
包含管理員建立的任何次要或臨時使用者。結果也
納入使用裝置的任何次要使用者 (或訪客使用者)
搜尋結果未包含工作資料夾,因為這不是工作資料夾
次要使用者以下範例說明如何使用這個方法:
Kotlin
// The device is stored for the night. Stop all running secondary users. dpm.getSecondaryUsers(adminName).forEach { dpm.stopUser(adminName, it) }
Java
// The device is stored for the night. Stop all running secondary users. for (UserHandle user : dpm.getSecondaryUsers(adminName)) { dpm.stopUser(adminName, user); }
您也可以使用以下方法找出次要使用者的狀態:
DevicePolicyManager.isEphemeralUser()
- 從次要使用者的管理員呼叫這個方法,瞭解帳戶是否 。
DevicePolicyManager.isAffiliatedUser()
- 從次要使用者的管理員呼叫這個方法,確認該使用者 與主要使用者相關聯如要進一步瞭解關聯,請參閱 DPC 協調方式。
使用者管理
如要完全管理使用者生命週期,您可以針對應用程式的生命週期 精細控管裝置變更使用者的時間和方式。舉例來說, 可以在裝置閒置一段時間後刪除使用者,或者,您也可以 在消費者的輪班完成前,將未送出的訂單傳送至伺服器。
登出
Android 9.0 在螢幕鎖定畫面上新增登出按鈕,讓使用 裝置才能結束工作階段。只要輕觸按鈕,系統就會停止 次要使用者,如果是暫時使用者,則刪除該使用者,然後主要使用者返回 移到前景主要使用者位於以下位置時,Android 會隱藏按鈕 否則主要使用者無法登出
根據預設,Android 不會顯示結束工作階段按鈕,而是您的管理員 (
全代管裝置) 呼叫
DevicePolicyManager.setLogoutEnabled()
。如果需要
確認按鈕目前狀態、呼叫
DevicePolicyManager.isLogoutEnabled()
。
次要使用者的管理員能夠透過程式,將使用者登出並返回
提供給主要使用者首先,請確認次要使用者和主要使用者
,然後呼叫 DevicePolicyManager.logoutUser()
。如果
系統會將登出的使用者視為臨時使用者,則會停止,然後刪除
內容。
切換使用者
如要切換到其他次要使用者,全代管裝置的管理員可以
呼叫 DevicePolicyManager.switchUser()
。為了方便起見
可以傳遞 null
,切換至主要使用者。
停止使用者
如要阻止次要使用者,擁有全代管裝置的 DPC 可以呼叫
DevicePolicyManager.stopUser()
。如果停止的使用者為
臨時使用者,系統會停止該使用者,然後刪除該使用者。
建議您盡可能停止使用者,避免將數量維持在裝置的容量以下 執行使用者人數上限。
刪除使用者
如要永久刪除次要使用者,DPC 可呼叫下列其中一項
DevicePolicyManager
方法:
- 全代管裝置的管理員可以呼叫
removeUser()
。 - 次要使用者的管理員可以呼叫
wipeData()
。
系統會在臨時使用者登出、停止或切換後刪除使用者
停用預設 UI
如果您的 DPC 提供管理使用者的 UI,您可以停用 Android 內建的
多使用者介面方法是呼叫
DevicePolicyManager.setLogoutEnabled()
,然後新增
DISALLOW_USER_SWITCH
限制,如
範例:
Kotlin
// Explicitly disallow logging out using Android UI (disabled by default). dpm.setLogoutEnabled(adminName, false) // Disallow switching users in Android's UI. This DPC can still // call switchUser() to manage users. dpm.addUserRestriction(adminName, UserManager.DISALLOW_USER_SWITCH)
Java
// Explicitly disallow logging out using Android UI (disabled by default). dpm.setLogoutEnabled(adminName, false); // Disallow switching users in Android's UI. This DPC can still // call switchUser() to manage users. dpm.addUserRestriction(adminName, UserManager.DISALLOW_USER_SWITCH);
裝置使用者無法透過 Android 內建 UI 新增次要使用者
因為全代管裝置的管理員會自動新增
DISALLOW_ADD_USER
使用者限制。
工作階段訊息
當使用裝置的使用者切換到新使用者時,Android 會顯示一個面板, 醒目顯示切換按鈕Android 會顯示下列訊息:
- 裝置切換至次要執行個體時顯示的啟動使用者工作階段訊息 從主要使用者中移除
- 裝置返回主要使用者時顯示的使用者工作階段訊息 來自次要使用者
系統不會在兩個次要使用者之間切換時顯示訊息。
由於郵件不一定適合所有情況,您可以變更 顯示這些訊息的文字舉例來說,如果您的解決方案使用臨時使用者 可以在訊息中說明這一點,例如:「停止瀏覽器」 工作階段和正在刪除個人資料...
系統會持續顯示訊息幾秒鐘,因此每一則訊息
就是簡短且明確的詞組如要自訂訊息,管理員可呼叫
DevicePolicyManager
方法
setStartUserSessionMessage()
和
setEndUserSessionMessage()
,如
範例:
Kotlin
// Short, easy-to-read messages shown at the start and end of a session. // In your app, store these strings in a localizable resource. internal val START_USER_SESSION_MESSAGE = "Starting guest session…" internal val END_USER_SESSION_MESSAGE = "Stopping & clearing data…" // ... dpm.setStartUserSessionMessage(adminName, START_USER_SESSION_MESSAGE) dpm.setEndUserSessionMessage(adminName, END_USER_SESSION_MESSAGE)
Java
// Short, easy-to-read messages shown at the start and end of a session. // In your app, store these strings in a localizable resource. private static final String START_USER_SESSION_MESSAGE = "Starting guest session…"; private static final String END_USER_SESSION_MESSAGE = "Stopping & clearing data…"; // ... dpm.setStartUserSessionMessage(adminName, START_USER_SESSION_MESSAGE); dpm.setEndUserSessionMessage(adminName, END_USER_SESSION_MESSAGE);
請傳送 null
,刪除自訂訊息並返回 Android 預設值
訊息。如要查看目前的訊息文字,請撥打
getStartUserSessionMessage()
或
getEndUserSessionMessage()
。
您的裝置政策控制器 (DPC) 應設定本地化訊息 以使用者目前所在地區的語言為準您也必須更新訊息, 使用者的語言代碼變更:
Kotlin
override fun onReceive(context: Context?, intent: Intent?) { // Added the <action android:name="android.intent.action.LOCALE_CHANGED" /> // intent filter for our DeviceAdminReceiver subclass in the app manifest file. if (intent?.action === ACTION_LOCALE_CHANGED) { // Android's resources return a string suitable for the new locale. getManager(context).setStartUserSessionMessage( getWho(context), context?.getString(R.string.start_user_session_message)) getManager(context).setEndUserSessionMessage( getWho(context), context?.getString(R.string.end_user_session_message)) } super.onReceive(context, intent) }
Java
public void onReceive(Context context, Intent intent) { // Added the <action android:name="android.intent.action.LOCALE_CHANGED" /> // intent filter for our DeviceAdminReceiver subclass in the app manifest file. if (intent.getAction().equals(ACTION_LOCALE_CHANGED)) { // Android's resources return a string suitable for the new locale. getManager(context).setStartUserSessionMessage( getWho(context), context.getString(R.string.start_user_session_message)); getManager(context).setEndUserSessionMessage( getWho(context), context.getString(R.string.end_user_session_message)); } super.onReceive(context, intent); }
裝置政策控制器 (DPC) 協調
管理次要使用者通常需要兩個 DPC 執行個體,而每個 DPC 執行個體 另一部是完全受管理的裝置,但另一部則為次要使用者所有。建立期間 新使用者,全代管裝置的管理員會設定另一個執行個體 以新使用者的身分
關聯使用者
這份開發人員指南中的部分 API 僅適用於次要使用者 有關係。由於 Android 停用了部分功能 (例如網路記錄) 您應盡快將使用者結盟。範例如下: 設定。
設定
先設定新的次要使用者 (透過擁有次要使用者的裝置政策控制器)
讓使用者輕鬆使用您可以前往
DeviceAdminReceiver.onEnabled()
回呼。如果您先前
設定對 createAndManageUser()
呼叫的任何管理員額外項目,就能取得
傳回 intent
引數的值。以下範例是 DPC 關聯
叫用回呼中的新的次要使用者:
Kotlin
override fun onEnabled(context: Context?, intent: Intent?) { super.onEnabled(context, intent) // Get the affiliation ID (our DPC previously put in the extras) and // set the ID for this new secondary user. intent?.getStringExtra(AFFILIATION_ID_KEY)?.let { val dpm = getManager(context) dpm.setAffiliationIds(getWho(context), setOf(it)) } // Continue setup of the new secondary user ... }
Java
public void onEnabled(Context context, Intent intent) { // Get the affiliation ID (our DPC previously put in the extras) and // set the ID for this new secondary user. String affiliationId = intent.getStringExtra(AFFILIATION_ID_KEY); if (affiliationId != null) { DevicePolicyManager dpm = getManager(context); dpm.setAffiliationIds(getWho(context), new HashSet<String>(Arrays.asList(affiliationId))); } // Continue setup of the new secondary user ... }
裝置政策控制器 (DPC) 之間的遠端程序呼叫 (RPC)
雖然這兩個 DPC 執行個體是在不同的使用者下執行,但 DPC
擁有裝置的使用者和次要使用者能夠互相通訊。
呼叫其他裝置政策控制器 (DPC) 的服務會跨越使用者界線,因此無法在 DPC 上
照常呼叫 bindService()
,
Android:如何繫結至
另一個使用者, 打給
DevicePolicyManager.bindDeviceAdminServiceAsUser()
。
您的 DPC 只能繫結至執行於
DevicePolicyManager.getBindDeviceAdminTargetUsers()
。
以下範例顯示次要使用者繫結的管理員
完全受管理的裝置:
Kotlin
// From a secondary user, the list contains just the primary user. dpm.getBindDeviceAdminTargetUsers(adminName).forEach { // Set up the callbacks for the service connection. val intent = Intent(mContext, FullyManagedDeviceService::class.java) val serviceconnection = object : ServiceConnection { override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) { // Call methods on service ... } override fun onServiceDisconnected(componentName: ComponentName) { // Clean up or reconnect if needed ... } } // Bind to the service as the primary user [it]. val bindSuccessful = dpm.bindDeviceAdminServiceAsUser(adminName, intent, serviceconnection, Context.BIND_AUTO_CREATE, it) }
Java
// From a secondary user, the list contains just the primary user. List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(adminName); if (targetUsers.isEmpty()) { // If the users aren't affiliated, the list doesn't contain any users. return; } // Set up the callbacks for the service connection. Intent intent = new Intent(mContext, FullyManagedDeviceService.class); ServiceConnection serviceconnection = new ServiceConnection() { @Override public void onServiceConnected( ComponentName componentName, IBinder iBinder) { // Call methods on service ... } @Override public void onServiceDisconnected(ComponentName componentName) { // Clean up or reconnect if needed ... } }; // Bind to the service as the primary user. UserHandle primaryUser = targetUsers.get(0); boolean bindSuccessful = dpm.bindDeviceAdminServiceAsUser( adminName, intent, serviceconnection, Context.BIND_AUTO_CREATE, primaryUser);
其他資源
如要進一步瞭解專用裝置,請參閱下列文件: