Cómo establecer configuraciones administradas

Si desarrollas apps para el mercado empresarial, es posible que debas satisfacer requisitos particulares establecidos por las políticas de una organización. Las configuraciones administradas, antes conocidas como restricciones de aplicaciones, permiten que el administrador de TI de la organización especifique de forma remota la configuración de las apps. Esta función es útil, sobre todo, para las apps aprobadas por la organización que se implementan en un perfil de trabajo.

Por ejemplo, una organización puede exigir que las apps aprobadas permitan al administrador de TI lo siguiente:

  • Cómo permitir o bloquear URLs en un navegador web
  • Configura si una app podrá sincronizar contenido con datos móviles o solo con Wi-Fi
  • Establece la configuración de correo electrónico de la app

En esta guía, se muestra cómo implementar la configuración administrada en tu app. Para ver apps de ejemplo con una configuración administrada, consulta ManagedConfigurations. Si eres desarrollador de administración de movilidad empresarial (EMM), consulta la guía de la API de Android Management.

Nota: Por razones históricas, estas opciones de configuración se conocen como restricciones y se implementan con archivos y clases que usan este término (como RestrictionsManager). Sin embargo, estas restricciones en realidad pueden implementar una amplia gama de opciones de configuración, no solo restricciones sobre las funciones de la app.

Descripción general de la configuración remota

Las apps definen opciones de configuración administrada que un administrador de TI puede establecer de manera remota. Estos son parámetros de configuración arbitrarios que un proveedor de configuración administrada puede modificar. Si tu app se ejecuta en un perfil de trabajo, el administrador de TI puede cambiar su configuración administrada.

El proveedor de configuraciones administradas es otra app que se ejecuta en el mismo dispositivo. Por lo general, el administrador de TI controla esta aplicación. El administrador de TI comunica los cambios de configuración a la app del proveedor de configuración administrada. Esa app, a su vez, cambia la configuración en la tuya.

Para proporcionar configuraciones administradas de forma externa, haz lo siguiente:

  • Declara las configuraciones administradas en el manifiesto de la app. Esto permite que el administrador de TI lea la configuración de la app a través de las APIs de Google Play.
  • Cada vez que se reanude la app, usa el objeto RestrictionsManager para verificar las configuraciones administradas actuales, y cambia la IU y el comportamiento de la app para que se ajusten a esas configuraciones.
  • Escucha el intent ACTION_APPLICATION_RESTRICTIONS_CHANGED. Cuando recibas esta transmisión, verifica RestrictionsManager para ver cuáles son las configuraciones administradas actuales y haz los cambios necesarios en el comportamiento de tu app.

Cómo definir opciones de configuración administradas

Tu app puede admitir cualquier configuración administrada que desees definir. Debes declarar las configuraciones administradas de la app en un archivo de configuración administrada y declarar el archivo de configuración en el manifiesto. La creación de un archivo de configuración permite que otras apps examinen las configuraciones administradas que proporciona tu app. Los socios de EMM pueden leer los parámetros de configuración de tu app con las APIs de Google Play.

Para definir las opciones de configuración remota de tu app, coloca el siguiente elemento en el elemento <application> de tu manifiesto:

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

Crea un archivo llamado app_restrictions.xml en el directorio res/xml de la app. La estructura de ese archivo se describe en la referencia de RestrictionsManager. El archivo tiene un único elemento <restrictions> de nivel superior, que contiene un elemento secundario <restriction> para cada opción de configuración que tiene la app.

Nota: No crees versiones localizadas del archivo de configuración administrada. Tu app solo puede tener un archivo de configuración administrado, por lo que las configuraciones serán coherentes para tu app en todas las configuraciones regionales.

En un entorno empresarial, un EMM suele usar el esquema de configuración administrada para generar una consola remota para los administradores de TI, de modo que los administradores puedan configurar tu aplicación de forma remota.

El proveedor de configuración administrada puede consultar la app para obtener detalles sobre las configuraciones disponibles, incluido su texto descriptivo. El proveedor de configuración y el administrador de TI pueden cambiar las opciones de configuración administradas de tu app en cualquier momento, incluso cuando esta no se está ejecutando.

Por ejemplo, supongamos que tu app se puede configurar de forma remota para permitir o prohibir que descargue datos a través de una conexión móvil. Tu app podría tener un elemento <restriction> como el siguiente:

<?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>

Usa el atributo android:key de cada configuración para leer su valor desde un paquete de configuración administrado. Por este motivo, cada configuración debe tener una string de clave única, la cual no se puede localizar. Debe especificarse con un literal de string.

Nota: En una app de producción, android:title y android:description deben extraerse de un archivo de recursos localizado, como se describe en Cómo localizar con recursos.

Una app define restricciones mediante paquetes dentro de un bundle_array. Por ejemplo, una app con varias opciones de conexión de VPN podría definir la configuración de cada servidor de VPN en una bundle, con varios paquetes agrupados en un array de paquetes:

<?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>

Los tipos admitidos para el elemento android:restrictionType se enumeran en la Tabla 1 y se documentan en la referencia de RestrictionsManager y RestrictionEntry.

Tabla 1: Tipos de entrada de restricciones y su uso.

Tipo android:restrictionType Uso común
TYPE_BOOLEAN "bool" Valor booleano, verdadero o falso.
TYPE_STRING "string" Es un valor de cadena, como un nombre.
TYPE_INTEGER "integer" Es un número entero con un valor de MIN_VALUE a MAX_VALUE.
TYPE_CHOICE "choice" Es un valor de cadena seleccionado de android:entryValues, que, por lo general, se presenta como una lista de selección única.
TYPE_MULTI_SELECT "multi-select" Un array de strings con valores seleccionados de android:entryValues Úsalo para presentar una lista de selección múltiple en la que se pueda seleccionar más de una entrada, por ejemplo, para elegir títulos específicos para incluir en la lista de entidades permitidas.
TYPE_NULL "hidden" Tipo de restricción oculto. Usa este tipo para la información que se debe transferir, pero que no se debería presentar al usuario en la IU. Almacena un solo valor de cadena.
TYPE_BUNDLE_ARRAY "bundle_array" Úsalo para almacenar arreglos de restricción bundles. Disponible en Android 6.0 (nivel de API 23).

Nota: android:entryValues son legibles por máquina y no se pueden localizar. Usa android:entries para presentar valores legibles que se puedan localizar. Cada entrada debe tener un índice correspondiente en android:entryValues.

Verifica las opciones de configuración administradas

Tu app no recibe notificaciones automáticamente cuando otras apps cambian sus parámetros de configuración. En cambio, debes comprobar cuáles son las configuraciones administradas cuando se inicia o se reanuda la app, y detectar un intent del sistema para averiguar si las configuraciones cambian mientras se ejecuta la app.

Para averiguar los parámetros de configuración actuales, tu app usa un objeto RestrictionsManager. Tu app debe verificar las configuraciones administradas actuales en los siguientes momentos:

Para obtener un objeto RestrictionsManager, obtén la actividad actual con getActivity() y, luego, llama al método Activity.getSystemService() de esa actividad:

Kotlin

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

Java

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

Una vez que tengas un RestrictionsManager, podrás llamar a su método getApplicationRestrictions() para obtener los parámetros de configuración actuales:

Kotlin

var appRestrictions: Bundle = myRestrictionsMgr.applicationRestrictions

Java

Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();

Nota: Para mayor comodidad, también puedes obtener la configuración actual con un UserManager llamando a UserManager.getApplicationRestrictions(). Este método se comporta exactamente igual que RestrictionsManager.getApplicationRestrictions().

El método getApplicationRestrictions() requiere la lectura desde el almacenamiento de datos, por lo que debe hacerse con moderación. No llames a este método cada vez que necesites conocer la configuración actual. En cambio, debes llamarla una vez cuando se inicie o reanude tu app, y almacenar en caché el paquete de configuraciones administradas recuperadas. Luego, busca el intent ACTION_APPLICATION_RESTRICTIONS_CHANGED para averiguar si la configuración cambia mientras la app está activa, como se describe en Escucha los cambios de configuración administrada.

Lee y aplica configuraciones administradas

El método getApplicationRestrictions() muestra un Bundle que contiene un par clave-valor para cada configuración que se estableció. Todos los valores son del tipo Boolean, int, String y String[]. Una vez que tengas las configuraciones administradas Bundle, puedes comprobar los ajustes de configuración actuales con los métodos Bundle estándar para esos tipos de datos, como getBoolean() o getString().

Nota: Las configuraciones administradas Bundle contienen un elemento por cada configuración establecida de forma explícita por un proveedor de configuraciones administradas. Sin embargo, no puedes suponer que una configuración estará presente en el paquete solo porque definiste un valor predeterminado en el archivo en formato XML de configuraciones administradas.

Depende de tu app tomar la medida adecuada en función de los parámetros de configuración administrados actuales. Por ejemplo, si tu app tiene una configuración que especifica si puede descargar datos a través de una conexión móvil y descubres que está establecida en false, tendrás que inhabilitar la descarga de datos, excepto cuando el dispositivo tenga una conexión Wi-Fi, como se muestra en el siguiente código de ejemplo:

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 varias restricciones anidadas, lee la entrada de restricción bundle_array como una colección de objetos Parcelable y transfórmala como un Bundle. En este ejemplo, los datos de configuración de cada VPN se analizan y se usan para crear una lista de opciones de conexión de servidores:

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
}

Detecta cambios en la configuración administrada

Cuando se cambian las configuraciones administradas de una app, el sistema activa el intent ACTION_APPLICATION_RESTRICTIONS_CHANGED. La app debe escuchar este intent para que puedas cambiar el comportamiento de la app cuando cambien los parámetros de configuración.

Nota: El intent ACTION_APPLICATION_RESTRICTIONS_CHANGED se envía solo a los objetos de escucha que se registran de forma dinámica, no a los que están declarados en el manifiesto de la app.

En el siguiente código, se muestra cómo registrar de forma dinámica un receptor de emisión para este 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);

Nota: Por lo general, no es necesario notificar a tu app sobre los cambios de configuración cuando se detiene. En cambio, debes cancelar el registro del receptor de emisión cuando la app está en pausa. Cuando se reanuda la app, primero debes verificar las configuraciones administradas actuales (como se explica en Cómo verificar la configuración administrada) y, luego, registrar el receptor de emisión para asegurarte de recibir notificaciones sobre los cambios de configuración que se producen mientras la app está activa.

Enviar comentarios sobre la configuración administrada a EMM

Después de aplicar los cambios de configuración administrada a tu app, se recomienda notificar a los EMM sobre el estado del cambio. Android admite una función llamada estados de apps con clave, que puedes usar para enviar comentarios cada vez que tu app intente aplicar cambios en la configuración administrada. Estos comentarios pueden actuar como confirmación de que tu app estableció correctamente las configuraciones administradas o pueden incluir un mensaje de error si tu app no pudo aplicar los cambios especificados.

Los proveedores de EMM pueden recuperar estos comentarios y mostrarlos en sus consolas para que los administradores de TI los vean. Consulta Cómo enviar comentarios sobre la app a EMM para obtener más información sobre el tema, incluida una guía detallada sobre cómo agregar compatibilidad con comentarios a tu app.

Muestras de código adicionales

En el ejemplo ManagedConfigurations, se demuestra aún más el uso de las APIs que se abordan en esta página.