En esta guía para desarrolladores, se explica cómo tu controlador de política de 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. Si tu DPC se ejecuta en un dispositivo completamente administrado, puedes crear y administrar dos tipos de usuarios:
- Los usuarios secundarios son usuarios de Android que tienen apps independientes y datos guardados entre sesiones. Administras al usuario con un componente de administrador. Estos usuarios son útiles en los casos en los que se toma 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.
Usarás tu DPC existente para administrar el dispositivo dedicado y los usuarios secundarios. Un componente de administrador en tu DPC se establece como administrador de 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.
Cómo crear usuarios
Tu DPC puede crear usuarios adicionales en segundo plano y, luego, cambiarlos al 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 el 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 comienza a ejecutarse, pero querrá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 el usuario nuevo con el 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 si detectas la excepción UserOperationException
y llamas a getUserOperationResult()
. Superar los límites de usuario son motivos comunes de fallas:
Crear un usuario puede llevar un tiempo. Si creas usuarios con frecuencia, puedes mejorar la experiencia 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 recomendarle un número de serie persistente. No conserves el UserHandle
, ya que el sistema los recicla a medida que creas y borras usuarios. Llama a UserManager.getSerialNumberForUser()
para obtener el número de serie:
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 actualizaciones y las instala, le solicita al usuario que agregue una Cuenta de Google junto con los servicios de Google y establece un bloqueo de pantalla. Esto puede tardar un poco y es posible que no se aplique 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 configuras esta marca, el usuario nuevo solo contendrá el conjunto mínimo de apps que necesita el teléfono para operar, por lo general, un navegador de archivos, un marcador telefónico, contactos y mensajes SMS.
Sigue el ciclo de vida del usuario
Tu DPC (si es un administrador del dispositivo completamente administrado) puede resultarte útil para saber cuándo cambian los usuarios secundarios. Para ejecutar tareas subsiguientes después de los cambios, anula estos métodos de devolución de llamada en la subclase DeviceAdminReceiver
de tu DPC:
onUserStarted()
- Se llama después de que el sistema inicia un usuario. Es posible que este usuario aún esté configurando 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 lo detuvo. 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 recibe la devolución de llamada. Puedes obtener el usuario del argumento
newUser
. onUserRemoved()
- Se llama después de que el sistema borra un usuario. Debido a que 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 al 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
.
Descubre usuarios
Para obtener todos los usuarios secundarios, un administrador de un dispositivo completamente administrado puede llamar a DevicePolicyManager.getSecondaryUsers()
. Los resultados incluyen a los usuarios secundarios o efímeros que creó el administrador. Los resultados también incluyen a los usuarios secundarios (o los usuarios invitados) que una persona que usa el dispositivo podría 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 del administrador de un usuario secundario para averiguar si se trata de un usuario efímero.
DevicePolicyManager.isAffiliatedUser()
- Llama a este método del 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 la sección Coordinación de DPC a continuación.
Administración de usuarios
Si quieres administrar por completo el ciclo de vida del usuario, puedes llamar a las APIs para controlar en detalle cuándo y cómo cambia el dispositivo de los usuarios. 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 finalice 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, luego, 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.
Android no muestra el botón de finalización de 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 cerrar la sesión del usuario 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 detendrá y, luego, lo borrará.
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
para cambiar al usuario principal.
Cómo detener a 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 detener a los usuarios siempre que sea posible para ayudar a no superar 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, se detienen o se alejan de ellos.
Inhabilita 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 la sesión del 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 el texto de estos mensajes. Por ejemplo, si tu solución usa sesiones de usuario efímeras, puedes reflejar esto en mensajes como los siguientes: Stoping navegador session and delete personal data... (Detener la sesión del navegador y borrar los datos personales).
El sistema muestra el mensaje durante solo un par de 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 del mensaje actual, llama a getStartUserSessionMessage()
o getEndUserSessionMessage()
.
Tu DPC debe configurar 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 del DPC
Por lo general, la administración de usuarios secundarios necesita dos instancias de tu DPC: una que posee el dispositivo completamente administrado y la otra, al usuario secundario. Cuando se crea un usuario nuevo, el administrador del dispositivo completamente administrado configura otra instancia de sí mismo 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. Debido a que Android inhabilita algunas funciones (por ejemplo, el registro de red) cuando agregas nuevos usuarios secundarios no afiliados al dispositivo, debes afiliar los usuarios lo antes posible. Consulta el ejemplo en Configuración a continuación.
Configuración
Configura nuevos usuarios secundarios (del DPC que posee al usuario secundario) antes de permitir que las personas los usen. Puedes realizar esta configuración desde la devolución de llamada DeviceAdminReceiver.onEnabled()
. Si ya configuraste recursos adicionales de administrador en la llamada a createAndManageUser()
, puedes obtener los valores del argumento intent
. En el siguiente ejemplo, se muestra un DPC que afiliado 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 supera los límites del usuario, tu DPC no puede llamar a bindService()
como lo harías normalmente en Android. Para realizar la vinculación a 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 con el 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 exclusivos, consulta los siguientes documentos:
- La descripción general de dispositivos dedicados es una descripción general de los dispositivos dedicados.
- El modo de tareas bloqueadas explica cómo bloquear un dispositivo dedicado en una sola app o en un conjunto de apps.
- Guía de soluciones de dispositivos exclusivos con más ejemplos para restringir los dispositivos dedicados y mejorar la experiencia del usuario.