En esta guía para desarrolladores, se explica cómo el controlador de políticas del dispositivo (DPC) puede administrar varios usuarios de Android en dispositivos dedicados.
Descripción general
Tu DPC puede ayudar a varias personas a compartir un solo dispositivo dedicado. Tu DPC que se ejecuta en un dispositivo completamente administrado puede crear y administrar dos tipos de usuarios:
- Los usuarios secundarios son usuarios de Android con apps independientes y datos que se guardan entre sesiones. Administras el usuario con un componente de administrador. Estos usuarios son útiles en casos en los que se levanta un dispositivo al comienzo de un turno, como los repartidores o los trabajadores de seguridad.
- Los usuarios efímeros son usuarios secundarios que el sistema borra cuando se detiene, cambia de dispositivo o se reinicia el dispositivo. Estos usuarios son útiles para casos en los que los datos se pueden borrar después de que finaliza la sesión, como los kioscos de Internet de acceso público.
Usas tu DPC existente para administrar el dispositivo dedicado y los usuarios secundarios. Un componente de administrador de tu DPC se establece como administrador para los nuevos usuarios secundarios cuando los creas.
Los administradores de un usuario secundario deben pertenecer al mismo paquete que el administrador del dispositivo completamente administrado. Para simplificar el desarrollo, te recomendamos que compartas un administrador entre el dispositivo y los usuarios secundarios.
La administración de varios usuarios en dispositivos dedicados suele requerir Android 9.0. Sin embargo, algunos de los métodos que se usan en esta guía para desarrolladores están disponibles en versiones anteriores de Android.
Usuarios secundarios
Los usuarios secundarios pueden conectarse a Wi-Fi y configurar redes nuevas. Sin embargo, no pueden editar ni borrar redes, ni siquiera las que crearon.
Cómo crear usuarios
Tu DPC puede crear usuarios adicionales en segundo plano y, luego, cambiarlos a primer plano. El proceso es casi el mismo para los usuarios secundarios y los efímeros. Implementa los siguientes pasos para los administradores del dispositivo completamente administrado y del usuario secundario:
- Llama a
DevicePolicyManager.createAndManageUser()
. Para crear un usuario efímero, incluyeMAKE_USER_EPHEMERAL
en el argumento de las marcas. - Llama a
DevicePolicyManager.startUserInBackground()
para iniciar el usuario en segundo plano. El usuario comenzará a ejecutarse, pero deberás finalizar la configuración antes de llevarlo al primer plano y mostrárselo a la persona que usa el dispositivo. - En el administrador del usuario secundario, llama a
DevicePolicyManager.setAffiliationIds()
para afiliar al usuario nuevo al usuario principal. Consulta Coordinación de DPC a continuación. - En el administrador del dispositivo completamente administrado, llama a
DevicePolicyManager.switchUser()
para cambiar al usuario al primer plano.
En el siguiente ejemplo, se muestra cómo puedes agregar el paso 1 a tu 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... } }
Cuando creas o inicias un usuario nuevo, puedes verificar el motivo de las fallas. Para ello, captura la excepción UserOperationException
y llama a getUserOperationResult()
. Superar los límites del usuario son motivos comunes de fallas:
Crear un usuario puede llevar algún tiempo. Si creas usuarios con frecuencia, puedes mejorar la experiencia del usuario si preparas a un usuario listo para usar en segundo plano. Es posible que debas equilibrar las ventajas de un usuario listo para usar con la cantidad máxima de usuarios permitidos en un dispositivo.
Identificación
Después de crear un usuario nuevo, debes dirigirlo con un número de serie persistente. No conserves el UserHandle
, ya que el sistema los recicla a medida que creas y borras usuarios. Para obtener el número de serie, llama a 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 ... }
Configuración del usuario
Según las necesidades de tus usuarios, puedes personalizar la configuración de los usuarios secundarios. Puedes incluir las siguientes marcas cuando llames a createAndManageUser()
:
SKIP_SETUP_WIZARD
- Omite la ejecución del asistente de configuración para usuarios nuevos que busca e instala actualizaciones, le solicita al usuario que agregue una Cuenta de Google junto con los servicios de Google y establece un bloqueo de pantalla. Esto puede demorar un tiempo y podría no aplicarse a todos los usuarios, como los kioscos de Internet públicos.
LEAVE_ALL_SYSTEM_APPS_ENABLED
- Deja habilitadas todas las apps del sistema en el usuario nuevo. Si no estableces esta marca, el usuario nuevo contendrá solo el conjunto mínimo de apps que el teléfono necesita para operar (por lo general, un navegador de archivos, un marcador telefónico, contactos y mensajes SMS).
Sigue el ciclo de vida del usuario
Es posible que tu DPC (si es un administrador del dispositivo completamente administrado) te resulte útil saber cuándo cambian los usuarios secundarios. Para ejecutar tareas de seguimiento después de los cambios, anula estos métodos de devolución de llamada en la subclase DeviceAdminReceiver
de tu DPC:
onUserStarted()
- Se invoca después de que el sistema inicia un usuario. Es posible que este usuario aún esté realizando la configuración o ejecutándose en segundo plano. Puedes obtener el usuario del argumento
startedUser
. onUserSwitched()
- Se llama después de que el sistema cambia a un usuario diferente. Puedes obtener el usuario nuevo que ahora se ejecuta en primer plano desde el argumento
switchedUser
. onUserStopped()
- Se llama después de que el sistema detiene a un usuario porque salió de su cuenta, cambió a un usuario nuevo (si el usuario es efímero) o tu DPC detuvo al usuario. Puedes obtener el usuario del argumento
stoppedUser
. onUserAdded()
- Se llama cuando el sistema agrega un usuario nuevo. Por lo general, los usuarios secundarios no están configurados por completo cuando tu DPC obtiene la devolución de llamada. Puedes obtener el usuario del argumento
newUser
. onUserRemoved()
- Se llama después de que el sistema borra un usuario. Como el usuario ya se borró, no puedes acceder al usuario representado por el argumento
removedUser
.
Para saber cuándo el sistema lleva a un usuario a primer plano o lo envía a segundo plano, las apps pueden registrar un receptor para las transmisiones ACTION_USER_FOREGROUND
y ACTION_USER_BACKGROUND
.
Descubrir usuarios
Para obtener todos los usuarios secundarios, un administrador de un dispositivo completamente administrado puede llamar a DevicePolicyManager.getSecondaryUsers()
. Los resultados incluyen a todos los usuarios secundarios o efímeros que creó el administrador. Los resultados también incluyen los usuarios secundarios (o un usuario invitado) que una persona que usa el dispositivo pueda haber creado. Los resultados no incluyen perfiles de trabajo porque no son usuarios secundarios. En el siguiente ejemplo, se muestra cómo puedes usar este método:
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); }
Estos son otros métodos a los que puedes llamar para averiguar el estado de los usuarios secundarios:
DevicePolicyManager.isEphemeralUser()
- Llama a este método desde el administrador de un usuario secundario para averiguar si se trata de un usuario efímero.
DevicePolicyManager.isAffiliatedUser()
- Llama a este método desde el administrador de un usuario secundario para averiguar si este está afiliado al usuario principal. Para obtener más información sobre la afiliación, consulta Coordinación con DPC a continuación.
Administración de usuarios
Si deseas administrar completamente el ciclo de vida del usuario, puedes llamar a las APIs para tener un control detallado de cuándo y cómo el dispositivo cambia de usuario. Por ejemplo, puedes borrar un usuario cuando no se usó un dispositivo durante un período o puedes enviar pedidos no enviados a un servidor antes de que termine el turno de una persona.
Salir
Android 9.0 agregó un botón de salida a la pantalla de bloqueo para que una persona que usa el dispositivo pueda finalizar su sesión. Después de presionar el botón, el sistema detiene al usuario secundario, borra al usuario si es efímero y el usuario principal regresa al primer plano. Android oculta el botón cuando el usuario principal está en primer plano porque este no puede salir de la cuenta.
Android no muestra el botón de finalización de la sesión de forma predeterminada, pero el administrador (de un dispositivo completamente administrado) puede habilitarlo llamando a DevicePolicyManager.setLogoutEnabled()
. Si necesitas confirmar el estado actual del botón, llama a DevicePolicyManager.isLogoutEnabled()
.
El administrador de un usuario secundario puede salir de su sesión de manera programática y regresar al usuario principal. Primero, confirma que los usuarios secundarios y principales estén afiliados y, luego, llama a DevicePolicyManager.logoutUser()
. Si el usuario que salió de su cuenta es efímero, el sistema se detiene y, luego, lo borra.
Cambiar usuarios
Para cambiar a un usuario secundario diferente, el administrador de un dispositivo completamente administrado puede llamar a DevicePolicyManager.switchUser()
. Para tu comodidad, puedes pasar null
a fin de cambiar al usuario principal.
Cómo detener un usuario
Para detener a un usuario secundario, un DPC que posee un dispositivo completamente administrado puede llamar a DevicePolicyManager.stopUser()
. Si el usuario detenido es un usuario efímero, se detiene y, luego, se borra.
Recomendamos que detengas los usuarios siempre que sea posible para mantener un nivel inferior a la cantidad máxima de usuarios activos del dispositivo.
Eliminar un usuario
Para borrar de forma permanente un usuario secundario, un DPC puede llamar a uno de los siguientes métodos DevicePolicyManager
:
- Un administrador de un dispositivo completamente administrado puede llamar a
removeUser()
. - Un administrador del usuario secundario puede llamar a
wipeData()
.
El sistema borra los usuarios efímeros cuando salen de su cuenta, cuando se los detiene o cuando salen de ella.
Cómo inhabilitar la IU predeterminada
Si tu DPC proporciona una IU para administrar usuarios, puedes inhabilitar la interfaz multiusuario integrada de Android. Para ello, llama a DevicePolicyManager.setLogoutEnabled()
y agrega la restricción DISALLOW_USER_SWITCH
como se muestra en el siguiente ejemplo:
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);
La persona que usa el dispositivo no puede agregar usuarios secundarios con la IU integrada de Android porque los administradores de dispositivos completamente administrados agregan automáticamente la restricción de usuarios de DISALLOW_ADD_USER
.
Mensajes de la sesión
Cuando la persona que usa un dispositivo cambia a un usuario nuevo, Android muestra un panel para destacar el interruptor. Android muestra los siguientes mensajes:
- Mensaje de inicio de sesión de usuario que se muestra cuando el dispositivo cambia a un usuario secundario del usuario principal
- Mensaje de sesión de usuario final que se muestra cuando el dispositivo regresa al usuario principal desde un usuario secundario
El sistema no muestra los mensajes cuando se alterna entre dos usuarios secundarios.
Dado que los mensajes podrían no ser adecuados para todas las situaciones, puedes cambiar su texto. Por ejemplo, si tu solución usa sesiones de usuario efímeras, puedes reflejarlo en los mensajes como Stopping phone session and delete personal data... (Detén la sesión del navegador y borra datos personales...)
El sistema muestra el mensaje solo durante unos segundos, por lo que cada mensaje debe ser una frase corta y clara. Para personalizar los mensajes, el administrador puede llamar a los métodos setStartUserSessionMessage()
y setEndUserSessionMessage()
de DevicePolicyManager
, como se muestra en el siguiente ejemplo:
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);
Pasa null
para borrar tus mensajes personalizados y volver a los mensajes predeterminados de Android. Si necesitas verificar el texto actual del mensaje, llama a getStartUserSessionMessage()
o getEndUserSessionMessage()
.
Tu DPC debe establecer mensajes localizados para la configuración regional actual del usuario. También deberás actualizar los mensajes cuando cambie la configuración regional del usuario:
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); }
Coordinación de DPC
Por lo general, la administración de usuarios secundarios necesita dos instancias de tu DPC: una es la propietaria del dispositivo completamente administrado y la otra, del usuario secundario. Cuando se crea un usuario nuevo, el administrador del dispositivo completamente administrado establece otra instancia como administrador del usuario nuevo.
Usuarios afiliados
Algunas de las APIs de esta guía para desarrolladores solo funcionan cuando los usuarios secundarios están afiliados. Dado que Android inhabilita algunas funciones (por ejemplo, el registro de red) cuando agregas nuevos usuarios secundarios no afiliados al dispositivo, debes afiliar a los usuarios lo antes posible. Consulta el ejemplo en Configuración a continuación.
Configuración
Configura nuevos usuarios secundarios (desde el DPC que posee al usuario secundario) antes de permitir que las personas los usen. Puedes hacerlo desde la devolución de llamada DeviceAdminReceiver.onEnabled()
. Si ya configuraste recursos adicionales del administrador en la llamada a createAndManageUser()
, puedes obtener los valores del argumento intent
. En el siguiente ejemplo, se muestra un DPC que afiliada a un nuevo usuario secundario en la devolución de llamada:
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 entre DPC
Aunque las dos instancias de DPC se ejecutan con usuarios diferentes, los DPC que poseen el dispositivo y los usuarios secundarios pueden comunicarse entre sí.
Debido a que llamar al servicio de otro DPC cruza los límites del usuario, tu DPC no puede llamar a bindService()
como lo normalmente lo harías en Android. Para establecer una vinculación con un servicio que se ejecuta en otro usuario, llama a DevicePolicyManager.bindDeviceAdminServiceAsUser()
.
Tu DPC solo puede vincularse a servicios que se ejecutan en los usuarios que muestra DevicePolicyManager.getBindDeviceAdminTargetUsers()
.
En el siguiente ejemplo, se muestra el administrador de una vinculación de usuario secundaria al administrador del dispositivo completamente administrado:
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);
Recursos adicionales
Para obtener más información sobre los dispositivos dedicados, consulta los siguientes documentos:
- La descripción general de dispositivos dedicados es una descripción general de los dispositivos dedicados.
- En Modo de tareas bloqueadas, se explica cómo bloquear un dispositivo dedicado con una sola app o un conjunto de apps.
- Guía de soluciones para dispositivos exclusivos con más ejemplos para restringir los dispositivos dedicados y mejorar la experiencia del usuario.