В этом руководстве для разработчиков рассказывается, как с помощью контроллера правил для устройств (DPC) управлять несколькими пользователями Android на специализированных устройствах.
Обзор
Контроллер политики устройств позволяет нескольким пользователям работать на одном специализированном устройстве. DPC, запущенный на полностью управляемом устройстве, может создавать два типа пользователей и управлять ими:
- Дополнительные пользователи – это пользователи Android, у которых есть отдельные приложения и данные, сохраняемые между сеансами. Вы управляете пользователем с помощью компонента администратора. Такие пользователи удобны, когда устройство берется в начале смены, например водителями службы доставки или сотрудниками службы безопасности.
- Временные пользователи – это дополнительные пользователи, которых система удаляет, когда пользователь останавливает работу, переключается на другого пользователя или перезагружает устройство. Это удобно, если данные можно удалить после завершения сеанса, например в интернет-киосках общего доступа.
Для управления специализированным устройством и дополнительными пользователями вы используете существующий контроллер политики устройств. Компонент администратора в DPC назначает себя администратором для новых дополнительных пользователей при их создании.
Администраторы дополнительного пользователя должны принадлежать к тому же пакету, что и администратор полностью управляемого устройства. Чтобы упростить разработку, рекомендуем использовать одного администратора для устройства и дополнительных пользователей.
Для управления несколькими пользователями на специализированных устройствах обычно требуется Android 9.0, однако некоторые методы, описанные в этом руководстве для разработчиков, доступны в более ранних версиях Android.
Дополнительные пользователи
Дополнительные пользователи могут подключаться к Wi-Fi и настраивать новые сети. Однако они не могут изменять или удалять сети, даже созданные ими самими.
Создание пользователей
Контроллер может создавать дополнительных пользователей в фоновом режиме, а затем переключать их в активный режим. Процесс практически одинаков для дополнительных и временных пользователей. Выполните следующие действия в администраторах полностью управляемого устройства и дополнительного пользователя:
- Позвоните по номеру
DevicePolicyManager.createAndManageUser(). Чтобы создать временного пользователя, включитеMAKE_USER_EPHEMERALв аргумент flags. - Вызовите
DevicePolicyManager.startUserInBackground(), чтобы запустить пользователя в фоновом режиме. Пользователь начинает бежать, но вы захотите завершить настройку, прежде чем выводить пользователя на передний план и показывать его человеку, использующему устройство. - В администраторе дополнительного пользователя вызовите
DevicePolicyManager.setAffiliationIds(), чтобы связать нового пользователя с основным. Подробнее о координации с DPC… - Вернитесь в администратор полностью управляемого устройства и вызовите
DevicePolicyManager.switchUser(), чтобы переключить пользователя на передний план.
В примере ниже показано, как добавить шаг 1 в DPC.
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- Оставляет все системные приложения включенными для нового пользователя. Если вы не установите этот флаг, новый пользователь получит только минимальный набор приложений, необходимых для работы телефона, – обычно это файловый браузер, телефонный номеронабиратель, контакты и SMS-сообщения.
Отслеживайте жизненный цикл пользователей
Если контроллер политики устройств является администратором полностью управляемого устройства, ему может быть полезно знать, когда меняются дополнительные пользователи. Чтобы запустить последующие задачи после изменений, переопределите эти методы обратного вызова в подклассе DeviceAdminReceiver вашего DPC:
onUserStarted()- Вызывается после того, как система запускает пользователя. Возможно, настройка этого пользователя ещё не завершена или он работает в фоновом режиме. Вы можете получить пользователя из аргумента
startedUser. onUserSwitched()- Вызывается после того, как система переключается на другого пользователя. Вы можете получить нового пользователя,
который сейчас работает в активном режиме, из аргумента
switchedUser. onUserStopped()- Вызывается после того, как система останавливает пользователя, потому что он вышел из системы, переключился на нового пользователя (если пользователь временный) или ваш контроллер политик устройств остановил пользователя. Вы можете получить пользователя из аргумента
stoppedUser. onUserAdded()- Вызывается, когда система добавляет нового пользователя. Обычно к моменту получения обратного вызова контроллером политики устройств настройка дополнительных пользователей ещё не завершена. Вы можете получить пользователя из аргумента
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 ниже.
Управление доступом
Если вы хотите полностью управлять жизненным циклом пользователя, вы можете вызывать API для точного контроля того, когда и как устройство меняет пользователей. Например, вы можете удалить пользователя, если устройство не использовалось в течение определенного периода времени, или отправить все неотправленные заказы на сервер до окончания смены сотрудника.
Выйти
В Android 9.0 на заблокированном экране появилась кнопка выхода, позволяющая пользователю завершить сеанс. После нажатия кнопки система останавливает дополнительного пользователя, удаляет его, если он временный, и возвращает основного пользователя на передний план. Android скрывает кнопку, когда на переднем плане находится основной пользователь, поскольку он не может выйти из системы.
По умолчанию кнопка завершения сеанса не показывается, но администратор полностью управляемого устройства может включить ее с помощью метода DevicePolicyManager.setLogoutEnabled(). Если вам нужно подтвердить текущее состояние кнопки, вызовите DevicePolicyManager.isLogoutEnabled().
Администратор дополнительного пользователя может программно выйти из системы и вернуться к основному пользователю. Сначала убедитесь, что дополнительный и основной пользователи связаны, а затем вызовите метод DevicePolicyManager.logoutUser(). Если пользователь временный, система останавливает и удаляет его.
Переключение между аккаунтами
Чтобы переключиться на другого дополнительного пользователя, администратор полностью управляемого устройства может вызвать DevicePolicyManager.switchUser(). Для удобства вы можете передать null, чтобы переключиться на основного пользователя.
Как заблокировать пользователя
Чтобы остановить дополнительного пользователя, контроллер политики устройств, которому принадлежит полностью управляемое устройство, может вызвать DevicePolicyManager.stopUser(). Если остановленный пользователь является временным, он будет остановлен, а затем удален.
Рекомендуем по возможности останавливать пользователей, чтобы не превышать максимальное количество работающих пользователей на устройстве.
Удаление пользователя
Чтобы удалить дополнительного пользователя без возможности восстановления, контроллер политики устройств может вызвать один из следующих методов DevicePolicyManager:
- Администратор полностью управляемого устройства может вызвать
removeUser(). - Администратор дополнительного пользователя может вызвать
wipeData().
Система удаляет временных пользователей, когда они выходят из системы, останавливают работу или переключаются на другой аккаунт.
Отключение пользовательского интерфейса по умолчанию
Если в вашем контроллере есть интерфейс для управления пользователями, вы можете отключить встроенный интерфейс добавления пользователей 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, поскольку администраторы полностью управляемых устройств автоматически добавляют ограничение 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().
Контроллер должен задавать локализованные сообщения для текущего региона пользователя. Также необходимо обновлять сообщения при изменении региональных настроек пользователя:
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: один владеет полностью управляемым устройством, а другой – дополнительным пользователем. При создании нового пользователя администратор полностью управляемого устройства назначает администратором нового пользователя другой экземпляр самого себя.
Аффилированные пользователи
Некоторые 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 ... }
RPC между DPC
Несмотря на то что два экземпляра DPC запущены в разных аккаунтах, контроллеры, которым принадлежат устройство и дополнительные пользователи, могут взаимодействовать друг с другом.
Поскольку вызов сервиса другого контроллера политики устройства пересекает границы пользователей, ваш контроллер не может вызвать bindService() так, как это обычно делается в Android. Чтобы привязать сервис, запущенный в другом пользователе, вызовите DevicePolicyManager.bindDeviceAdminServiceAsUser().
Ваш контроллер политик на устройствах может связываться только с сервисами, запущенными в пользователях, возвращенных методом 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);
Дополнительные ресурсы
Чтобы узнать больше о специализированных устройствах, ознакомьтесь со следующими документами:
- Обзор специализированных устройств – это обзор специализированных устройств.
- В статье Режим блокировки задач рассказывается, как заблокировать специализированное устройство, чтобы на нем можно было использовать только одно приложение или определенный набор приложений.
- Руководство по специализированным устройствам с дополнительными примерами того, как ограничить специализированные устройства и повысить удобство для пользователей.