Perfis de trabalho

A plataforma Android permite que os dispositivos tenham perfis de trabalho (às vezes chamados de perfis gerenciados). Um perfil de trabalho é controlado por um administrador de TI, e a funcionalidade disponível para ele é definida separadamente da funcionalidade do perfil principal do usuário. Essa abordagem permite que as organizações controlem o ambiente em que apps e dados específicos da empresa são executados no dispositivo de um usuário, além de permitir que os usuários usem os apps e perfis pessoais.

Esta lição mostra como modificar seu aplicativo para que ele funcione de forma confiável em um dispositivo com um perfil de trabalho. Você não precisa fazer nada além das práticas recomendadas comuns de desenvolvimento de apps. No entanto, algumas dessas práticas recomendadas se tornam especialmente importantes em dispositivos com perfis de trabalho. Este documento destaca os problemas que você precisa conhecer.

Visão geral

Os usuários geralmente querem usar dispositivos pessoais em um ambiente corporativo. Essa situação pode apresentar um dilema às organizações. Se o usuário puder usar o próprio dispositivo, a organização precisará se preocupar com a presença das informações confidenciais, como e-mails e contatos dos funcionários, em um dispositivo que ela não controla.

Para solucionar essa situação, o Android 5.0 (API de nível 21) permite que as organizações configurem perfis de trabalho. Se um dispositivo tiver um perfil de trabalho, as configurações desse perfil estarão sob o controle do administrador de TI. O administrador de TI pode escolher quais apps são permitidos para esse perfil e pode controlar apenas quais recursos do dispositivo ficam disponíveis para o perfil.

Se um dispositivo tiver um perfil de trabalho, haverá implicações para os apps em execução no dispositivo, independentemente do perfil em que o app seja executado:

  • Por padrão, a maioria das intents não passa de um perfil para outro. Se um app em execução no perfil disparar uma intent, não haverá nenhum gerenciador para a intent nesse perfil e a intent não tenha permissão para passar para o outro perfil devido a restrições de perfil, a solicitação falhará e o app poderá ser desligado inesperadamente.
  • O administrador de TI do perfil pode limitar quais apps do sistema estão disponíveis no perfil de trabalho. Essa restrição também pode resultar na ausência de gerenciadores para algumas intents comuns no perfil de trabalho.
  • Como os perfis pessoal e de trabalho têm áreas de armazenamento separadas, um URI de arquivo válido em um perfil não é válido no outro. Qualquer intent disparada em um perfil pode ser processada no outro, dependendo das configurações de perfil. Portanto, não é seguro anexar URIs de arquivo a intents.

Evitar intents com falha

Em um dispositivo com um perfil de trabalho, há restrições sobre a possibilidade de as intents passarem de um perfil para outro. Na maioria dos casos, quando uma intent é acionada, ela é processada no mesmo perfil em que é acionada. Se não houver um gerenciador para a intent nesse perfil, ela não será processada, e o app que a acionou poderá ser encerrado inesperadamente, mesmo se houver um gerenciador para a intent no outro perfil.

O administrador do perfil pode escolher quais intents podem passar de um perfil para outro. Como o administrador de TI toma essa decisão, não há como saber com antecedência quais intents podem ultrapassar esse limite. O administrador de TI define essa política e pode alterá-la a qualquer momento.

Antes de o app iniciar uma atividade, é necessário verificar se há uma resolução adequada. Para verificar se há uma resolução aceitável, chame Intent.resolveActivity(). Se não houver como resolver a intent, o método retornará null. Se o método não retornar um valor não nulo, há pelo menos uma maneira de resolver a intent, e é seguro realizar a operação dela. Nesse caso, a intent pode ser resolvida porque há um gerenciador no perfil atual ou porque a intent pode passar para um gerenciador no outro perfil. Para mais informações sobre como resolver intents, consulte Intents comuns.

Por exemplo, se o app precisar definir timers, ele precisará verificar se há um gerenciador válido para a intent ACTION_SET_TIMER. Se o app não conseguir resolver a intent, ele precisará tomar a ação adequada, como mostrar uma mensagem de erro.

Kotlin

fun startTimer(message: String, seconds: Int) {

    // Build the "set timer" intent
    val timerIntent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(packageManager) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent)

    }
}

Java

public void startTimer(String message, int seconds) {

    // Build the "set timer" intent
    Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(getPackageManager()) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent);

    }
}

Compartilhar arquivos entre perfis

Às vezes, um app precisa conceder a outros apps acesso aos seus próprios arquivos. Por exemplo, um app de galeria de imagens pode querer compartilhar as imagens com editores de imagens. Normalmente, há duas maneiras de compartilhar um arquivo: com um URI de arquivo ou com um URI de conteúdo.

Um URI de arquivo começa com o prefixo file:, seguido pelo caminho absoluto do arquivo no armazenamento do dispositivo. No entanto, como o perfil de trabalho e o perfil pessoal usam áreas de armazenamento separadas, um URI de arquivo válido em um perfil não é válido no outro. Essa situação significa que, se você anexar um URI de arquivo a uma intent e ela for processada no outro perfil, o gerenciador não poderá acessar o arquivo.

Em vez disso, compartilhe arquivos com URIs de conteúdo. Os URIs de conteúdo identificam o arquivo de maneira mais segura e compartilhável. O URI de conteúdo contém o caminho do arquivo, mas também a autoridade que fornece o arquivo e um número de ID que o identifica. Gere um Content ID para qualquer arquivo usando um FileProvider. Depois, você pode compartilhar esse ID de conteúdo com outros apps (mesmo no outro perfil). O destinatário pode usar o ID do conteúdo para ter acesso ao arquivo.

Por exemplo, veja como conseguir o URI de conteúdo de um URI de arquivo específico:

Kotlin

// Open File object from its file URI
val fileToShare = File(fileUriToShare)

val contentUriToShare: Uri = FileProvider.getUriForFile(
        context,
        "com.example.myapp.fileprovider",
        fileToShare
)

Java

// Open File object from its file URI
File fileToShare = new File(fileUriToShare);

Uri contentUriToShare = FileProvider.getUriForFile(getContext(),
        "com.example.myapp.fileprovider", fileToShare);

Ao chamar o método getUriForFile(), é necessário incluir a autoridade do provedor de arquivos (neste exemplo, "com.example.myapp.fileprovider"), que é especificada no elemento <provider> do manifesto do app. Para saber mais sobre o compartilhamento de arquivos com URIs de conteúdo, consulte Como compartilhar arquivos.

Detectar notificações

Um app normalmente fornece uma subclasse NotificationListenerService para receber callbacks do sistema sobre mudanças nas notificações. Dispositivos com perfis de trabalho podem afetar a forma como o NotificationListenerService funciona com seu app.

Em um perfil de trabalho

Não é possível usar um NotificationListenerService de um app em execução no perfil de trabalho. Quando o app está em execução em um perfil de trabalho, o sistema ignora o NotificationListenerService do app. No entanto, apps em execução no perfil pessoal podem detectar notificações.

Em um perfil pessoal

Quando o app for executado no perfil pessoal, talvez você não receba notificações de apps em execução no perfil de trabalho. Por padrão, todos os apps com perfil pessoal recebem callbacks, mas um administrador de TI pode autorizar um ou mais apps desse tipo que possam detectar mudanças nas notificações. Em seguida, o sistema bloqueia apps que não estão na lista de permissões. No Android 8.0 (nível 26 da API) ou versões mais recentes, um controlador de política de dispositivo (DPC, na sigla em inglês) que gerencia um perfil de trabalho pode impedir que seu app detecte as notificações desse perfil usando o método setPermittedCrossProfileNotificationListeners() de DevicePolicyManager. Seu app ainda recebe callbacks sobre notificações postadas no perfil pessoal.

Testar a compatibilidade do app com perfis de trabalho

Teste seu app em um ambiente de perfil de trabalho para detectar problemas que causariam falhas no app em um dispositivo com perfis de trabalho. Especificamente, testar em um dispositivo com perfil de trabalho é uma boa maneira de garantir que o app processe intents corretamente: não disparar intents que não possam ser processadas, não anexar URIs que não funcionem entre perfis e assim por diante.

Fornecemos um app de exemplo, o TestDPC, que pode ser usado para configurar um perfil de trabalho em um dispositivo com o Android 5.0 (nível 21 da API) ou versões mais recentes. Esse app oferece uma maneira simples de testar seu app em um ambiente de perfil de trabalho. Você também pode usar esse app para configurar o perfil de trabalho da seguinte maneira:

  • Especificar quais aplicativos padrão estão disponíveis no perfil gerenciado
  • Configura quais intents têm permissão para passar de um perfil para outro

Se você instalar manualmente um app por um cabo USB em um dispositivo que tenha um perfil de trabalho, o app vai ser instalado tanto no perfil pessoal quanto no de trabalho. Depois de instalar o app, é possível testá-lo nas seguintes condições:

  • Se uma intent normalmente é processada por um app padrão (por exemplo, o app de câmera), tente desativar esse app padrão no perfil de trabalho e verifique se ele processa isso corretamente.
  • Se você disparar uma intent esperando que ela seja processada por outro app, tente ativar e desativar a permissão dessa intent para passar de um perfil para outro. Verifique se o app se comporta corretamente nas duas circunstâncias. Se a intent não tiver permissão para cruzar entre perfis, verifique o comportamento do app quando houver um gerenciador adequado no perfil. Por exemplo, se o app disparar uma intent relacionada ao mapa, tente cada um dos seguintes cenários:
    • O dispositivo permite que intents de mapa passem de um perfil para outro, e há um gerenciador adequado no outro perfil (o perfil em que o app não está sendo executado).
    • O dispositivo não permite que intents de mapa se cruzem entre perfis, mas há um gerenciador adequado no perfil do app.
    • O dispositivo não permite que intents de mapa passem entre perfis e não há um gerenciador adequado para intents de mapa no perfil do dispositivo.
  • Se você anexar conteúdo a uma intent, verifique se ela se comporta de maneira correta ao ser processada no perfil do app e ao cruzar entre perfis.

Testar perfis de trabalho: dicas e truques

Há alguns truques que podem ser úteis em testes em um dispositivo do perfil de trabalho.

  • Conforme observado, quando você carrega um app por sideload em um dispositivo com perfil de trabalho, ele é instalado nos dois perfis. Se quiser, você pode excluir o app de um perfil e deixá-lo no outro.
  • A maioria dos comandos do gerenciador de atividades disponíveis no shell do Android Debug Bridge (adb) oferece suporte à sinalização --user, que permite especificar com qual usuário executar a execução. Ao especificar um usuário, você pode escolher se quer executar como usuário principal não gerenciado ou como perfil de trabalho. Para mais informações, consulte Comandos de shell do adb.
  • Para encontrar os usuários ativos em um dispositivo, use o comando list users do gerenciador de pacotes adb. O primeiro número na string de saída é o ID do usuário, que pode ser usado com a sinalização --user. Para mais informações, consulte Comandos do shell do adb.

Por exemplo, para encontrar os usuários em um dispositivo, execute este comando:

$ adb shell pm list users
UserInfo{0:Drew:13} running
UserInfo{10:Work profile:30} running

Nesse caso, o usuário principal("Drew") tem o ID de usuário 0, e o perfil de trabalho tem o ID de usuário 10. Para executar um app no perfil de trabalho, use um comando como este:

$ adb shell am start --user 10 \
-n "com.example.myapp/com.example.myapp.testactivity" \
-a android.intent.action.MAIN -c android.intent.category.LAUNCHER