Definir configurações gerenciadas

Se você estiver desenvolvendo apps para o mercado corporativo, talvez seja necessário atender a requisitos específicos definidos pelas políticas de uma organização. As configurações gerenciadas, anteriormente conhecidas como restrições de aplicativos, permitem que o administrador de TI da organização especifique remotamente as configurações dos apps. Esse recurso é útil principalmente para apps aprovados pela organização implantados em um perfil de trabalho.

Por exemplo, uma organização pode exigir que os apps aprovados permitam que o administrador de TI:

  • Permitir ou bloquear URLs de um navegador da Web
  • Configurar se um app pode sincronizar conteúdo por dados móveis ou apenas por Wi-Fi
  • Configurar as configurações de e-mail do app

Este guia mostra como implementar configurações de configuração gerenciada no app. Para conferir apps de exemplo com uma configuração gerenciada, consulte Configurações gerenciadas. Se você é um desenvolvedor de gerenciamento de mobilidade empresarial (EMM), consulte o guia da API Android Management.

Observação:por motivos históricos, essas configurações são conhecidas como restrições e são implementadas com arquivos e classes que usam esse termo (como RestrictionsManager). No entanto, essas restrições podem implementar uma ampla gama de opções de configuração, não apenas restrições à funcionalidade do app.

Visão geral da configuração remota

Os apps definem as opções de configuração gerenciada que podem ser definidas remotamente por um administrador de TI. Essas são configurações arbitrárias que podem ser alteradas por um provedor de configuração gerenciada. Se o app estiver sendo executado em um perfil de trabalho, o administrador de TI poderá mudar a configuração gerenciada dele.

O provedor de configurações gerenciadas é outro app executado no mesmo dispositivo. Esse app geralmente é controlado pelo administrador de TI. O administrador de TI comunica as mudanças de configuração ao app gerenciado do provedor de configuração. Esse app, por sua vez, muda as configurações no seu app.

Para fornecer configurações gerenciadas externamente:

  • Declare as configurações gerenciadas no manifesto do app. Isso permite que o administrador de TI leia as configurações do app pelas APIs do Google Play.
  • Sempre que o app for retomado, use o objeto RestrictionsManager para verificar as configurações gerenciadas atuais e mudar a interface e o comportamento do app para se adequar a essas configurações.
  • Ouça a intent ACTION_APPLICATION_RESTRICTIONS_CHANGED. Quando você receber essa transmissão, verifique o RestrictionsManager para saber quais são as configurações gerenciadas atuais e faça as mudanças necessárias no comportamento do app.

Definir configurações gerenciadas

Seu app pode oferecer suporte a qualquer configuração gerenciada que você queira definir. Declare as configurações gerenciadas do app em um arquivo de configurações gerenciadas e declare o arquivo de configurações no manifesto. A criação de um arquivo de configuração permite que outros apps examinem as configurações gerenciadas fornecidas pelo seu app. Os parceiros de EMM podem ler as configurações do app usando as APIs do Google Play.

Para definir as opções de configuração remota do app, coloque o seguinte elemento no elemento <application> do manifesto:

<meta-data android:name="android.content.APP_RESTRICTIONS"
    android:resource="@xml/app_restrictions" />

Crie um arquivo chamado app_restrictions.xml no diretório res/xml do app. A estrutura desse arquivo é descrita na referência para RestrictionsManager. O arquivo tem um único elemento <restrictions> de nível superior, que contém um elemento filho <restriction> para cada opção de configuração do app.

Observação:não crie versões localizadas do arquivo de configuração gerenciado. Seu app só pode ter um arquivo de configurações gerenciadas, para que as configurações sejam consistentes em todos os locais.

Em um ambiente empresarial, um EMM geralmente usa o esquema de configuração gerenciada para gerar um console remoto para administradores de TI configurarem o aplicativo remotamente.

O provedor de configuração gerenciada pode consultar o app para encontrar detalhes sobre as configurações disponíveis, incluindo o texto da descrição. O provedor de configurações e o administrador de TI podem mudar as configurações gerenciadas do app a qualquer momento, mesmo quando ele não está em execução.

Por exemplo, suponha que seu app possa ser configurado remotamente para permitir ou proibir o download de dados por uma conexão de celular. O app pode ter um elemento <restriction> como este:

<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">

  <restriction
    android:key="downloadOnCellular"
    android:title="@string/download_on_cell_title"
    android:restrictionType="bool"
    android:description="@string/download_on_cell_description"
    android:defaultValue="true" />

</restrictions>

Use o atributo android:key de cada configuração para ler o valor de um pacote de configuração gerenciado. Por esse motivo, cada configuração precisa ter uma string de chave exclusiva, e a string não pode ser localizada. Ele precisa ser especificado com um literal de string.

Observação:em um app de produção, android:title e android:description precisam ser extraídos de um arquivo de recurso localizado, conforme descrito em Localização com recursos.

Um app define restrições usando pacotes em um bundle_array. Por exemplo, um app com várias opções de conexão VPN pode definir cada configuração de servidor VPN em um bundle, com vários pacotes agrupados em uma matriz de pacotes:

<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android" >

  <restriction
    android:key="vpn_configuration_list"
    android:restrictionType="bundle_array">
    <restriction
      android:key="vpn_configuration"
      android:restrictionType="bundle">
      <restriction
        android:key="vpn_server"
        android:restrictionType="string"/>
      <restriction
        android:key="vpn_username"
        android:restrictionType="string"/>
      <restriction
        android:key="vpn_password"
        android:restrictionType="string"/>
    </restriction>
  </restriction>

</restrictions>

Os tipos compatíveis com o elemento android:restrictionType estão listados na Tabela 1 e documentados na referência de RestrictionsManager e RestrictionEntry.

Tabela 1. Tipos de entrada e uso de restrições.

Tipo android:restrictionType Uso típico
TYPE_BOOLEAN "bool" Um valor booleano, verdadeiro ou falso.
TYPE_STRING "string" Um valor de string, como um nome.
TYPE_INTEGER "integer" Um número inteiro com um valor de MIN_VALUE a MAX_VALUE.
TYPE_CHOICE "choice" Um valor de string selecionado em android:entryValues, normalmente apresentado como uma lista de seleção única.
TYPE_MULTI_SELECT "multi-select" Uma matriz de string com valores selecionados de android:entryValues. Use isso para apresentar uma lista de seleção múltipla em que mais de uma entrada pode ser selecionada, como para escolher títulos específicos para a lista de permissões.
TYPE_NULL "hidden" Tipo de restrição oculta. Use esse tipo para informações que precisam ser transferidas, mas não podem ser apresentadas ao usuário na interface. Armazena um único valor de string.
TYPE_BUNDLE_ARRAY "bundle_array" Use para armazenar matrizes de restrição bundles. Disponível no Android 6.0 (nível 23 da API).

Observação:android:entryValues são legíveis por máquina e não podem ser localizadas. Use android:entries para apresentar valores legíveis por humanos que podem ser localizados. Cada entrada precisa ter um índice correspondente em android:entryValues.

Verificar as configurações gerenciadas

O app não é notificado automaticamente quando outros apps mudam as configurações. Em vez disso, você precisa verificar quais são as configurações gerenciadas quando o app é iniciado ou retomado e detectar uma intent do sistema para descobrir se as configurações mudam enquanto o app está em execução.

Para descobrir as configurações atuais, o app usa um objeto RestrictionsManager. O app precisa verificar as configurações gerenciadas atuais nos seguintes momentos:

Para receber um objeto RestrictionsManager, receba a atividade atual com getActivity() e, em seguida, chame o método Activity.getSystemService() dessa atividade:

Kotlin

var myRestrictionsMgr =
        activity?.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager

Java

RestrictionsManager myRestrictionsMgr =
    (RestrictionsManager) getActivity()
        .getSystemService(Context.RESTRICTIONS_SERVICE);

Depois de ter um RestrictionsManager, você pode acessar as configurações de configuração atuais chamando o método getApplicationRestrictions():

Kotlin

var appRestrictions: Bundle = myRestrictionsMgr.applicationRestrictions

Java

Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();

Observação:para sua conveniência, você também pode buscar as configurações atuais com um UserManager chamando UserManager.getApplicationRestrictions(). Esse método se comporta exatamente como RestrictionsManager.getApplicationRestrictions().

O método getApplicationRestrictions() exige a leitura do armazenamento de dados. Portanto, ele precisa ser usado com moderação. Não chame esse método sempre que precisar saber a configuração atual. Em vez disso, chame-o uma vez quando o app for iniciado ou retomado e armazene em cache o pacote de configurações gerenciadas buscado. Em seguida, detecte a intent ACTION_APPLICATION_RESTRICTIONS_CHANGED para descobrir se a configuração mudou enquanto o app estava ativo, conforme descrito em Detectar mudanças de configuração gerenciadas.

Como ler e aplicar configurações gerenciadas

O método getApplicationRestrictions() retorna um Bundle contendo um par de chave-valor para cada configuração definida. Os valores são todos do tipo Boolean, int, String e String[]. Depois de ter as configurações gerenciadas Bundle, é possível verificar as configurações atuais com os métodos Bundle padrão para esses tipos de dados, como getBoolean() ou getString().

Observação:as configurações gerenciadas Bundle contêm um item para cada configuração definida explicitamente por um provedor de configurações gerenciadas. No entanto, não é possível presumir que uma configuração vai estar presente no pacote só porque você definiu um valor padrão no arquivo XML de configurações gerenciadas.

Cabe ao app tomar as medidas adequadas com base nas configurações de configuração gerenciada atuais. Por exemplo, se o app tiver uma configuração que especifique se ele pode fazer o download de dados por uma conexão de celular e a configuração estiver definida como false, será necessário desativar o download de dados, exceto quando o dispositivo tiver uma conexão Wi-Fi, conforme mostrado no exemplo de código abaixo:

Kotlin

val appCanUseCellular: Boolean =
        if (appRestrictions.containsKey("downloadOnCellular")) {
            appRestrictions.getBoolean("downloadOnCellular")
        } else {
            // cellularDefault is a boolean using the restriction's default value
            cellularDefault
        }

if (!appCanUseCellular) {
    // ...turn off app's cellular-download functionality
    // ...show appropriate notices to user
}

Java

boolean appCanUseCellular;

if (appRestrictions.containsKey("downloadOnCellular")) {
    appCanUseCellular = appRestrictions.getBoolean("downloadOnCellular");
} else {
    // cellularDefault is a boolean using the restriction's default value
    appCanUseCellular = cellularDefault;
}

if (!appCanUseCellular) {
    // ...turn off app's cellular-download functionality
    // ...show appropriate notices to user
}

Para aplicar várias restrições aninhadas, leia a entrada de restrição bundle_array como uma coleção de objetos Parcelable e converta como Bundle. Neste exemplo, os dados de configuração de cada VPN são analisados e usados para criar uma lista de opções de conexão de servidor:

Kotlin

// VpnConfig is a sample class used store config data, not defined
val vpnConfigs = mutableListOf<VpnConfig>()

val parcelables: Array<out Parcelable>? =
        appRestrictions.getParcelableArray("vpn_configuration_list")

if (parcelables?.isNotEmpty() == true) {
    // iterate parcelables and cast as bundle
    parcelables.map { it as Bundle }.forEach { vpnConfigBundle ->
        // parse bundle data and store in VpnConfig array
        vpnConfigs.add(VpnConfig()
                .setServer(vpnConfigBundle.getString("vpn_server"))
                .setUsername(vpnConfigBundle.getString("vpn_username"))
                .setPassword(vpnConfigBundle.getString("vpn_password")))
    }
}

if (vpnConfigs.isNotEmpty()) {
    // ...choose a VPN configuration or prompt user to select from list
}

Java

// VpnConfig is a sample class used store config data, not defined
List<VpnConfig> vpnConfigs = new ArrayList<>();

Parcelable[] parcelables =
    appRestrictions.getParcelableArray("vpn_configuration_list");

if (parcelables != null && parcelables.length > 0) {
    // iterate parcelables and cast as bundle
    for (int i = 0; i < parcelables.length; i++) {
        Bundle vpnConfigBundle = (Bundle) parcelables[i];
        // parse bundle data and store in VpnConfig array
        vpnConfigs.add(new VpnConfig()
            .setServer(vpnConfigBundle.getString("vpn_server"))
            .setUsername(vpnConfigBundle.getString("vpn_username"))
            .setPassword(vpnConfigBundle.getString("vpn_password")));
    }
}

if (!vpnConfigs.isEmpty()) {
    // ...choose a VPN configuration or prompt user to select from list
}

Detectar mudanças de configuração gerenciada

Sempre que as configurações gerenciadas de um app são alteradas, o sistema dispara a intent ACTION_APPLICATION_RESTRICTIONS_CHANGED. O app precisa detectar essa intent para que você possa mudar o comportamento dele quando as configurações mudarem.

Observação:a intent ACTION_APPLICATION_RESTRICTIONS_CHANGED é enviada apenas para listeners registrados dinamicamente, não para listeners declarados no manifesto do app.

O código a seguir mostra como registrar dinamicamente um broadcast receiver para essa intent:

Kotlin

val restrictionsFilter = IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)

val restrictionsReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {

        // Get the current configuration bundle
        val appRestrictions = myRestrictionsMgr.applicationRestrictions

        // Check current configuration settings, change your app's UI and
        // functionality as necessary.
    }
}

registerReceiver(restrictionsReceiver, restrictionsFilter)

Java

IntentFilter restrictionsFilter =
    new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);

BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
  @Override public void onReceive(Context context, Intent intent) {

    // Get the current configuration bundle
    Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();

    // Check current configuration settings, change your app's UI and
    // functionality as necessary.
  }
};

registerReceiver(restrictionsReceiver, restrictionsFilter);

Observação:normalmente, o app não precisa ser notificado sobre mudanças de configuração quando está pausado. Em vez disso, cancele o registro do broadcast receiver quando o app for pausado. Quando o app é retomado, você primeiro verifica as configurações gerenciadas atuais (conforme discutido em Verificar configurações gerenciadas) e depois registra o broadcast receiver para garantir que você seja notificado sobre mudanças de configuração que ocorrem enquanto o app está ativo.

Enviar feedback de configuração gerenciada para EMMs

Depois de aplicar as mudanças de configuração gerenciada ao app, é recomendável notificar os EMMs sobre o status da mudança. O Android oferece suporte a um recurso chamado estados de app com chave, que pode ser usado para enviar feedback sempre que o app tentar aplicar mudanças de configuração gerenciada. Esse feedback pode servir como confirmação de que o app definiu as configurações gerenciadas com sucesso ou pode incluir uma mensagem de erro se o app não conseguir aplicar as mudanças especificadas.

Os provedores de EMM podem recuperar esse feedback e exibi-lo nos consoles para que os administradores de TI possam conferir. Consulte Enviar feedback do app para EMMs para mais informações sobre o assunto, incluindo um guia detalhado sobre como adicionar suporte a feedback ao seu app.

Outros exemplos de código

O exemplo ManagedConfigurations demonstra melhor o uso das APIs abordadas nesta página.

Adicionar/remover apps da lista de permissões no perfil pessoal

As lojas de apps de terceiros podem expressar interesse em usar as configurações gerenciadas para ter uma maneira confiável de aplicar uma lista de permissões ou de bloqueio de apps ao perfil pessoal e ao recurso Espaço particular do consumidor, que é um espaço pessoal adicional para os usuários manterem os apps sensíveis. Se você desenvolve uma app store para uso empresarial e gostaria de usar esse recurso, envie este formulário para expressar seu interesse e selecione Interest in 3P app store allowlisting como Reason for Response no formulário.