Создание многопользовательских приложений

Когда устройство поддерживает нескольких пользователей , его приложения должны получать информацию об этих отдельных пользователях.

Некоторые приложения должны иметь некоторые компоненты, работающие как синглтоны, и могут принимать запросы от любого пользователя. В настоящее время эту функцию могут использовать только системные приложения.

Этот объект:

  • Экономит ресурсы
  • Арбитражирует один или несколько общих ресурсов между пользователями
  • Снижает нагрузку на сеть за счет использования одного подключения к серверу.

См. диаграмму ниже для изображения потока разрешений с несколькими пользователями.

Поток разрешений нескольких пользователей

Рисунок 1. Разрешения для нескольких пользователей

Включение одноэлементного компонента

Чтобы идентифицировать приложение как одноэлементное, добавьте android:singleUser=”true” к службе, получателю или поставщику в манифесте Android.

Система создаст экземпляр этого компонента в процессе, работающем только от имени пользователя 0. Любые запросы на подключение к этому провайдеру или службе или на трансляцию на этот приемник от любого пользователя будут направляться процессу пользователя 0. Если это единственный компонент в вашем приложении, будет запущен только один экземпляр вашего приложения.

Действия в вашем пакете по-прежнему будут запускаться в отдельном процессе для каждого пользователя с UID, находящимся в диапазоне UID для этого пользователя (например, 1010034).

Взаимодействие с пользователями

Установить разрешения

Эти разрешения необходимы

INTERACT_ACROSS_USERS (signature|system)
INTERACT_ACROSS_USERS_FULL (signature)

Используйте API

Используйте следующие API, чтобы приложения знали о нескольких пользователях.

  1. Извлеките дескриптор пользователя из входящих вызовов Binder:
    • int userHandle = UserHandle.getCallingUserId()
  2. Используйте новые защищенные API для запуска сервисов, активностей, трансляций для конкретного пользователя:
    • Context.startActivityAsUser(Intent, UserHandle)
    • Context.bindServiceAsUser(Intent, …, UserHandle)
    • Context.sendBroadcastAsUser(Intent, … , UserHandle)
    • Context.startServiceAsUser(Intent, …, UserHandle)
    UserHandle может быть явным пользователем или одним из специальных дескрипторов: UserHandle.CURRENT или UserHandle.ALL . CURRENT указывает пользователя, который в данный момент находится на переднем плане. Используйте ALL , если вы хотите отправить широковещательную рассылку всем пользователям.
  3. Связь с компонентами в вашем собственном приложении: (INTERACT_ACROSS_USERS) Или с компонентами в других приложениях: (INTERACT_ACROSS_USERS_FULL)
  4. Возможно, вам потребуется создать прокси-компоненты, которые запускаются в процессе пользователя, а затем получают доступ к компоненту singleUser пользователя 0.
  5. Запрашивайте пользователей и их дескрипторы с помощью новой системной службы UserManager :
    • UserManager.getUsers()
    • UserManager.getUserInfo()
    • UserManager.supportsMultipleUsers()
    • UserManager.getUserSerialNumber(int userHandle) — непереработанный номер, соответствующий дескриптору пользователя.
    • UserManager.getUserHandle(int serialNumber)
    • UserManager.getUserProfiles() — возвращает коллекцию собственных и управляемых профилей, если таковые имеются.
  6. Зарегистрируйтесь, чтобы прослушивать определенных или всех пользователей и обратные вызовы с помощью новых API-интерфейсов на ContentObserver, PackageMonitor, BroadcastReceiver, которые предоставляют дополнительную информацию о том, какой пользователь вызвал обратный вызов.

Услуги для нескольких пользователей или профилей

Не всем службам нужно запускать экземпляр в другом пользовательском или рабочем профиле. Если вашей системной службе нужно работать только от имени пользователя 0, отключите компоненты службы при запуске под другими пользователями, чтобы сохранить ресурсы. В следующем примере показано, как это можно сделать в точках входа службы:

// Add on all entry points such as boot_completed or other manifest-listed receivers and providers
if (!UserManager.isSystemUser()) {
    // Disable the service
    ComponentName targetServiceName = new ComponentName(this, TargetService.class);
    context.getPackageManager().setComponentEnabledSetting(
        targetServiceName, COMPONENT_ENABLED_STATE_DISABLED, 0);
}

В примере также можно использовать PackageManager.setApplicationEnabledSetting() для отключения всего приложения.