Compila apps que admitan varios usuarios

Cuando un dispositivo admite varios usuarios, sus apps deben estar al tanto de estos usuarios distintos.

Algunas apps deben tener algunos componentes que se ejecuten como objetos singleton y que puedan aceptar solicitudes de cualquier usuario. Actualmente, solo las apps del sistema pueden usar esta función.

Esta instalación tiene las siguientes características:

  • Conserva recursos
  • Arbitra uno o más recursos compartidos entre los usuarios
  • Reduce la sobrecarga de red mediante el uso de una sola conexión de servidor.

Consulta el siguiente diagrama para ver una representación del flujo de permisos con varios usuarios.

Flujo de permisos de varios usuarios

Figura 1: Permisos de varios usuarios

Habilita un componente singleton

Para identificar una app como singleton, agrega android:singleUser="true" a tu servicio, receptor o proveedor en el manifiesto de Android.

El sistema crea una instancia de ese componente en el proceso que se ejecuta solo como usuario 0. Cualquier solicitud para conectarse a ese proveedor o servicio, o para transmitir a ese receptor, desde cualquier usuario se enruta al proceso en el usuario 0. Si este es el único componente de tu app, solo se ejecutará una instancia de la app.

Las actividades de tu paquete se inician en un proceso independiente para cada usuario, y el UID se encuentra en el rango de UID de ese usuario (como 1010034).

Interactuar con los usuarios

Configurar permisos

Se requieren los siguientes permisos:

INTERACT_ACROSS_USERS (signature|system)
INTERACT_ACROSS_USERS_FULL (signature)

Usar APIs

Usa las siguientes APIs para que las apps detecten varios usuarios.

  1. Extrae el identificador de usuario de las llamadas entrantes de Binder:
    • int userHandle = UserHandle.getCallingUserId()
  2. Usa nuevas APIs protegidas para iniciar servicios, actividades y transmisiones en un usuario específico:
    • Context.startActivityAsUser(Intent, UserHandle)
    • Context.bindServiceAsUser(Intent, …, UserHandle)
    • Context.sendBroadcastAsUser(Intent, … , UserHandle)
    • Context.startServiceAsUser(Intent, …, UserHandle)
    UserHandle puede ser un usuario explícito o uno de los identificadores especiales: UserHandle.CURRENT o UserHandle.ALL. CURRENT indica el usuario que está en primer plano. Usa ALL cuando quieras enviar una transmisión a todos los usuarios.
  3. Comunicarse con componentes de tu propia app: (INTERACT_ACROSS_USERS) O con componentes de otras apps: (INTERACT_ACROSS_USERS_FULL)
  4. Es posible que debas crear componentes de proxy que se ejecuten en el proceso del usuario y que, luego, accedan al componente singleUser en el usuario 0.
  5. Consulta a los usuarios y sus identificadores con el nuevo servicio del sistema UserManager:
    • UserManager.getUsers()
    • UserManager.getUserInfo()
    • UserManager.supportsMultipleUsers()
    • UserManager.getUserSerialNumber(int userHandle): Es un número no reciclado que corresponde a un identificador de usuario.
    • UserManager.getUserHandle(int serialNumber)
    • UserManager.getUserProfiles(): Muestra la colección de perfiles autoadministrados y administrados, si los hay.
  6. Regístrate para escuchar a usuarios específicos o a todos los usuarios y las devoluciones de llamada con nuevas APIs en ContentObserver, PackageMonitor y BroadcastReceiver que proporcionan información adicional sobre qué usuario causó la devolución de llamada.

Servicios en varios usuarios o perfiles

No todos los servicios necesitan ejecutar una instancia en otro usuario o perfil de trabajo. Si el servicio del sistema solo necesita ejecutarse como el usuario 0, inhabilita los componentes del servicio cuando se ejecute en otros usuarios para ayudar a preservar los recursos. En el siguiente ejemplo, se muestra cómo puedes hacerlo en los puntos de entrada de tu servicio:

// 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);
}

El ejemplo también podría usar PackageManager.setApplicationEnabledSetting() para inhabilitar toda la app.