Gerenciar vários usuários

Este guia do desenvolvedor explica como o controlador de política de dispositivo (DPC) pode gerenciar vários usuários do Android em dispositivos dedicados;

Visão geral

Seu DPC pode ajudar várias pessoas a compartilhar um único dispositivo dedicado. Seu DPC em um dispositivo totalmente gerenciado pode criar e gerenciar dois tipos de usuários:

  • Os usuários secundários são usuários do Android com apps e dados separados salvos. entre sessões. Você gerencia o usuário com um componente de administrador. Esses usuários são útil para casos em que um dispositivo é selecionado no início de um turno, como motoristas de entregas ou trabalhadores de segurança.
  • Os usuários efêmeros são secundários e são excluídos pelo sistema quando parar, desligar ou o dispositivo será reinicializado. Esses usuários são úteis para casos em que os dados podem ser excluídos após o término da sessão, por exemplo, de acesso público quiosques de Internet.
.

Você usa seu DPC existente para gerenciar o dispositivo dedicado e o servidor usuários. Um componente administrador no DPC se define como administrador da nova instância secundária usuários ao criá-los.

Usuário principal e dois usuários secundários.
Figura 1. Usuários principais e secundários gerenciados por administradores de o mesmo DPC

Os administradores de um usuário secundário precisam pertencer ao mesmo pacote que o administrador da um dispositivo totalmente gerenciado. Para simplificar o desenvolvimento, recomendamos compartilhar um papel entre o dispositivo e os usuários secundários.

O gerenciamento de vários usuários em dispositivos dedicados normalmente exige o Android 9.0, No entanto, alguns dos métodos usados neste guia do desenvolvedor estão disponíveis em com as versões anteriores do Android.

Usuários secundários

Usuários secundários podem se conectar ao Wi-Fi e configurar novas redes. No entanto, eles não podem editar ou excluir redes, nem mesmo as criadas.

Criar usuários

Seu DPC pode criar usuários adicionais em segundo plano e trocá-los em primeiro plano. O processo é quase o mesmo para processos usuários temporários. Implemente as etapas a seguir para os administradores da conta dispositivo gerenciado e usuário secundário:

  1. Chame DevicePolicyManager.createAndManageUser(). Para criar um usuário temporário, inclua MAKE_USER_EPHEMERAL no argumento de sinalizações.
  2. Ligação DevicePolicyManager.startUserInBackground() para iniciar o usuário em segundo plano. O usuário começa a correr, mas você vai querer para concluir a configuração antes de levar o usuário ao primeiro plano e mostrá-lo ao a pessoa que está usando o dispositivo.
  3. No administrador do usuário secundário, chame DevicePolicyManager.setAffiliationIds() para afiliar o novo usuário ao usuário principal. Consulte Coordenação de DPC abaixo.
  4. De volta ao administrador do dispositivo totalmente gerenciado, chame DevicePolicyManager.switchUser() para alternar o usuário para primeiro plano.

O exemplo a seguir mostra como adicionar a etapa 1 ao 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...
  }
}

Ao criar ou iniciar um novo usuário, é possível verificar o motivo das falhas capturando a exceção UserOperationException e chamando getUserOperationResult() Exceder o limite de usuários são motivos comuns de falha:

.

A criação de um usuário pode levar algum tempo. Se você cria usuários com frequência, pode melhoram a experiência do usuário preparando um usuário pronto para uso em segundo plano. Talvez seja necessário equilibrar as vantagens de um usuário pronto para uso com o máximo número de usuários permitidos em um dispositivo.

Identificação

Após criar um novo usuário, consulte o usuário com um número de série persistente número Não manter o UserHandle, porque o sistema os recicla à medida que você criar e excluir usuários. Para descobrir o número de série, faça uma chamada 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  ...
}

Configuração do usuário

Dependendo das necessidades dos usuários, você pode personalizar a configuração de relatórios usuários. É possível incluir as seguintes flags ao chamar createAndManageUser():

SKIP_SETUP_WIZARD
Ignora a execução do assistente de configuração para novos usuários que verifica e instala atualizações, solicita que o usuário adicione uma Conta do Google junto com os serviços do Google e define um bloqueio de tela. Isso pode levar algum tempo e talvez não se aplique a todos usuários, como quiosques de Internet pública, por exemplo.
LEAVE_ALL_SYSTEM_APPS_ENABLED
Deixa todos os apps do sistema ativados no novo usuário. Se você não definir essa sinalização, o novo usuário contém apenas o conjunto mínimo de aplicativos de que o telefone precisa operar, normalmente um navegador de arquivos, discador de telefone, contatos e mensagens SMS.

Seguir o ciclo de vida do usuário

Seu DPC (se for um administrador do dispositivo totalmente gerenciado) pode achar útil para saber quando os usuários secundários mudam. Para executar tarefas de acompanhamento após as alterações, substitua estes métodos de callback na subclasse DeviceAdminReceiver do DPC:

onUserStarted()
Chamada depois que o sistema inicia um usuário. Talvez esse usuário ainda esteja configurando ou em segundo plano. É possível acessar o usuário do startedUser .
onUserSwitched()
Chamada após o sistema alternar para um usuário diferente. É possível conseguir o novo usuário que está sendo executado em primeiro plano pelo argumento switchedUser.
onUserStopped()
Chamada depois que o sistema interrompe um usuário porque ele saiu da conta, mudou para um novo usuário (se o usuário for efêmero), ou o DPC interrompeu o usuário. Você pode receber o usuário do argumento stoppedUser.
onUserAdded()
Chamado quando o sistema adiciona um novo usuário. Normalmente, os usuários secundários não são esteja totalmente configurado quando o DPC receber o callback. Você pode receber o usuário argumento newUser.
onUserRemoved()
Chamada depois que o sistema exclui um usuário. Como o usuário já foi excluído, não será possível acessar o usuário representado pelo argumento removedUser.

Para saber quando o sistema coloca um usuário em primeiro plano ou envia um usuário ao segundo plano, os aplicativos podem registrar um receptor para o ACTION_USER_FOREGROUND e ACTION_USER_BACKGROUND.

Descobrir usuários

Para reunir todos os usuários secundários, o administrador de um dispositivo totalmente gerenciado pode chamar DevicePolicyManager.getSecondaryUsers() Resultados inclua os usuários secundários ou temporários criados pelo administrador. Os resultados também incluir usuários secundários (ou um usuário convidado) que uma pessoa que usa o dispositivo pode tenham criado. Os resultados não incluem perfis de trabalho porque secundários. O exemplo a seguir mostra como usar esse 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);
}

Aqui estão outros métodos que você pode chamar para descobrir o status de usuários secundários:

DevicePolicyManager.isEphemeralUser()
Chame este método do administrador de um usuário secundário para descobrir se este é um usuário temporário.
DevicePolicyManager.isAffiliatedUser()
Chame este método do administrador de um usuário secundário para descobrir se esse usuário está afiliadas ao usuário principal. Para saber mais sobre afiliação, consulte DPC coordenação de equipe abaixo.

Gerenciamento de usuários

Se quiser gerenciar completamente o ciclo de vida do usuário, você pode chamar APIs para um controle preciso de quando e como o dispositivo muda os usuários. Por exemplo, pode excluir um usuário quando um dispositivo não for usado por um período de tempo, ou você pode enviar pedidos não enviados a um servidor antes que o turno de uma pessoa termine.

Sair

O Android 9.0 adiciona um botão de logout à tela de bloqueio para que uma pessoa usando o do dispositivo podem encerrar a sessão. Depois de tocar no botão, o sistema interrompe o usuário secundário, exclui o usuário se for temporário, e o usuário principal retorna em primeiro plano. O Android oculta o botão quando o usuário principal está no porque o usuário principal não pode sair.

O Android não mostra o botão de encerrar a sessão por padrão, mas o administrador (de uma totalmente gerenciado) podem ativá-lo chamando DevicePolicyManager.setLogoutEnabled() Se você precisar confirmar o estado atual do botão, chamar DevicePolicyManager.isLogoutEnabled()

O administrador de um usuário secundário pode desconectar o usuário programaticamente e retornar para o usuário principal. Primeiro, confirme se os usuários secundários e principais são afiliados, chame DevicePolicyManager.logoutUser(). Se o usuário desconectado é um usuário temporário, o sistema para e exclui o usuário.

Trocar de usuário

Para alternar para um usuário secundário diferente, o administrador de um dispositivo totalmente gerenciado pode: chame DevicePolicyManager.switchUser(). Para sua conveniência, pode transmitir null para alternar para o usuário principal.

Interromper um usuário

Para impedir um usuário secundário, um DPC dono de um dispositivo totalmente gerenciado pode chamar DevicePolicyManager.stopUser() Se o usuário interrompido for um temporário, ele é interrompido e excluído.

Recomendamos interromper os usuários sempre que possível para ajudar a permanecer abaixo do dispositivo o número máximo de usuários em execução.

Excluir um usuário

Para excluir permanentemente um usuário secundário, um DPC pode chamar uma das seguintes opções DevicePolicyManager métodos:

  • O administrador de um dispositivo totalmente gerenciado pode chamar removeUser().
  • Um administrador do usuário secundário pode chamar wipeData().

O sistema exclui usuários temporários quando eles são desconectados, interrompidos ou trocados. se afastar.

Desativar a interface padrão

Caso seu DPC ofereça uma interface para gerenciar usuários, é possível desativar o multiusuário. Você pode fazer isso chamando DevicePolicyManager.setLogoutEnabled() e adicionar o DISALLOW_USER_SWITCH conforme mostrado no exemplo a seguir:

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

O usuário do dispositivo não pode adicionar usuários secundários com a interface integrada do Android porque os administradores de dispositivos totalmente gerenciados Restrição de usuário do DISALLOW_ADD_USER.

Mensagens da sessão

Quando a pessoa que usa um dispositivo muda para um novo usuário, o Android mostra um painel para destaque a chave. O Android mostra as seguintes mensagens:

  • Mensagem de início da sessão do usuário exibida quando o dispositivo alterna para uma instância secundária do usuário principal.
  • Mensagem da sessão do usuário final exibida quando o dispositivo retorna para o usuário principal de um usuário secundário.

O sistema não mostra as mensagens ao alternar entre dois usuários secundários.

Como as mensagens podem não ser adequadas para todas as situações, você pode alterar o texto dessas mensagens. Por exemplo, se sua solução usar um usuário temporário sessões, você pode refletir isso em mensagens como: Parando o navegador sessão e excluindo dados pessoais...

O sistema mostra a mensagem por alguns segundos, então cada mensagem deve ser uma frase curta e clara. Para personalizar as mensagens, o administrador pode ligar para os métodos DevicePolicyManager setStartUserSessionMessage() e setEndUserSessionMessage(), como mostrado no exemplo a seguir:

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

Transmita null para excluir suas mensagens personalizadas e voltar ao padrão do Android. e envio de mensagens. Se você precisar verificar o texto da mensagem atual, chame getStartUserSessionMessage() ou getEndUserSessionMessage()

Seu DPC deve definir mensagens localizadas para a localidade atual do usuário. Você também precisa atualizar as mensagens quando o alterações de localidade do usuário:

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

Coordenação de DPC

O gerenciamento de usuários secundários normalmente precisa de duas instâncias do seu DPC, uma que seja proprietária o dispositivo totalmente gerenciado, enquanto o outro é proprietário do usuário secundário. Ao criar um novo usuário, o administrador do dispositivo totalmente gerenciado define outra instância do como administrador do novo usuário.

Usuários afiliados

Algumas das APIs neste guia do desenvolvedor só funcionam quando os usuários secundários são afiliados. Como o Android desativa alguns recursos (por exemplo, registro de rede) quando você adiciona novos usuários secundários não afiliados ao do dispositivo, afilie-os assim que possível. Confira o exemplo no Configuração abaixo.

Configurar

Configure novos usuários secundários (do DPC proprietário do usuário secundário) antes permitindo que as pessoas os usem. Você pode fazer essa configuração a partir do Callback DeviceAdminReceiver.onEnabled(). Se você definir os extras do administrador na chamada para createAndManageUser(), você vai receber o valores do argumento intent. O exemplo a seguir mostra um endereço de e-mail um novo usuário secundário no callback:

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 ...
}

RPCs entre DPCs

Mesmo que as duas instâncias do DPC sejam executadas com usuários separados, os DPCs proprietários do dispositivo e os usuários secundários podem se comunicar entre si. Como chamar outro serviço do DPC ultrapassa os limites do usuário, seu DPC não pode chame bindService() como você normalmente faria em Android. Para se vincular a um serviço em execução no outro usuário, chame DevicePolicyManager.bindDeviceAdminServiceAsUser()

Usuário principal e dois usuários secundários afiliados fazendo chamadas de RPCs.
Figura 2. Administradores de usuários principais e secundários afiliados chamar métodos do serviço

Seu DPC só pode se vincular a serviços em execução nos usuários retornados pelo DevicePolicyManager.getBindDeviceAdminTargetUsers() O exemplo a seguir mostra o administrador de uma vinculação de usuário secundária ao administrador do dispositivo totalmente gerenciado:

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

Outros recursos

Para saber mais sobre dispositivos dedicados, leia os seguintes documentos: