Gerenciar funções de aplicativos

O recurso de funções de aplicativo permite que um administrador de TI conceda privilégios especiais a um aplicativo gerenciado em um dispositivo Android. Ao atribuir um papel específico, um app pode ser isento de restrições de energia e de segundo plano, suspensão, hibernação (no Android 14 e versões mais recentes) e ter os controles do usuário (ou seja, inclui ações do usuário, como forçar o fechamento e limpar os dados do app) desativados (no Android 11 e versões mais recentes), permitindo que ele execute a função essencial sem interrupções. Além disso, o app pode ser notificado sobre os papéis atribuídos, o que permite que ele se inicialize sem intervenção do usuário.

Para uma lista de papéis de aplicativo disponíveis e os privilégios especiais concedidos a cada papel, consulte RoleType.

Pré-requisitos

O dispositivo é gerenciado por um EMM baseado na AMAPI (EMMs que usam um DPC personalizado não são compatíveis).

Preparar o app para usar o recurso

A integração com o SDK da AMAPI só é necessária se o app quiser ser notificado sobre as funções atribuídas, o que permite que ele se inicialize (ou seja, seja iniciado automaticamente sem interação do usuário).

Integrar o SDK da AMAPI ao seu app

Para mais informações sobre o SDK da AMAPI e como adicioná-lo ao seu app, consulte o guia de integração do SDK da AMAPI.

Adicionar os metadados necessários ao manifesto do app

O Android Device Policy (ADP) precisa saber o ComponentName da sua classe que implementa NotificationReceiverService para notificar o app sobre as funções atribuídas. É preciso marcar o serviço em AndroidManifest.xml de maneira adequada para que ele possa ser descoberto automaticamente pelo ADP.

  • O app precisa ter exatamente um serviço que seja enabled e tenha meta-data com android:name igual a com.google.android.managementapi.notification.NotificationReceiverService.SERVICE_APP_ROLES.
  • Este serviço precisa ter android:exported definido como true
  • O android:value do meta-data precisa ser definido como uma string vazia.
<service
 android:name=".MyNotificationReceiverService"
 android:exported="true">
    <meta-data android:name="com.google.android.managementapi.notification.NotificationReceiverService.SERVICE_APP_ROLES" android:value="" />
</service>

Se você estiver testando o papel COMPANION_APP, adicione também o seguinte meta-data ao seu serviço para que a política do dispositivo Android possa enviar atualizações de status de comandos locais para seu app:

<meta-data android:name="com.google.android.managementapi.notification.NotificationReceiverService.SERVICE_COMMAND_STATUS" android:value="" />

Crie um serviço que estenda NotificationReceiverService (ou atualize o atual)

Crie ou atualize o NotificationReceiverService atual e implemente um AppRolesListener para detectar as funções atribuídas ao app. Somente getAppRolesListener() é necessário para detectar as funções atribuídas ao app. Se o app receber uma função COMPANION_APP, você também precisará implementar getCommandListener():

import android.util.Log
import com.google.android.managementapi.approles.AppRolesListener
import com.google.android.managementapi.approles.model.AppRolesSetRequest
import com.google.android.managementapi.approles.model.AppRolesSetResponse
import com.google.android.managementapi.commands.CommandListener
import com.google.android.managementapi.commands.model.Command
import com.google.android.managementapi.notification.NotificationReceiverService

class MyNotificationReceiverService : NotificationReceiverService() {

  // If your app wants to listen for roles assigned
  override fun getAppRolesListener(): AppRolesListener =
    object : AppRolesListener {
      override fun onAppRolesSet(request: AppRolesSetRequest): AppRolesSetResponse {
        val roleTypes = request.roles.map { role -> role.roleType }
        Log.i(TAG, "onAppRolesSet: $roleTypes")

        return AppRolesSetResponse.getDefaultInstance()
      }
    }

 // If your app wants to listen for local command status updates
 // Only relevant for COMPANION_APP role
 override fun getCommandListener(): CommandListener {
    return object : CommandListener {
      override fun onCommandStatusChanged(command: Command) {
         Log.i(TAG, "onCommandStatusChanged")
      }
    }
  }

  private companion object {
    const val TAG = "MyNotificationReceiverService"
  }
}

O app pode ser notificado várias vezes se as funções mudarem várias vezes. Se todas as funções forem removidas, o app ainda será notificado com uma lista vazia de funções. Essa notificação vai tirar o app do estado interrompido e, com as isenções concedidas, ele poderá ser inicializado sem qualquer interação do usuário. Graças à notificação e às isenções de papéis do app, ele pode detectar transmissões ACTION_BOOT_COMPLETED. Se o app depende das configurações gerenciadas para inicializar, consulte Definir configurações gerenciadas para saber como ler e detectar mudanças.

A partir do SDK da AMAPI 1.9.0, cada função no AppRolesSetRequest também inclui o Role.ExtraCapabilities que o admin de TI concedeu na política. Por exemplo, um app complementar pode verificar se tem permissão para emitir um comando de limpeza local:

import com.google.android.managementapi.approles.model.AppRolesSetRequest
import com.google.android.managementapi.approles.model.AppRolesSetResponse
import com.google.android.managementapi.common.model.Role
...

  override fun onAppRolesSet(request: AppRolesSetRequest): AppRolesSetResponse {
    val canWipeLocally =
      request.roles.any { role ->
        role.roleType == Role.RoleType.COMPANION_APP &&
          role.extraCapabilities.localDeviceWipeCommand ==
            Role.ExtraCapabilities.LocalDeviceWipeCommand.LOCAL_DEVICE_WIPE_COMMAND_ALLOWED
      }
    Log.i(TAG, "onAppRolesSet: canWipeLocally=$canWipeLocally")

    return AppRolesSetResponse.getDefaultInstance()
  }

Somente o admin de TI pode conceder recursos extras. Se você definir extraCapabilities em um papel que transmitir para EnvironmentClient, a chamada vai falhar com um InvalidArgumentException.

Provisionar o dispositivo com políticas de funções de app

Os desenvolvedores de apps podem testar a atribuição de funções ao aplicativo usando um EMM ou seguindo o guia de início rápido da API Android Management. O notebook do Colab da AMAPI permite registrar uma empresa, criar uma política e provisionar um dispositivo.

Definir a política para seu app com funções de app

Configure um policy com as funções que seu app deve ter usando o ApplicationPolicy.roles.

O exemplo a seguir mostra como configurar o papel para apps MTD:

{
  "applications": [

    {
      "packageName": "com.example.mtd",
      "installType": "FORCE_INSTALLED",
      "roles": [
        { "roleType": "MOBILE_THREAT_DEFENSE_ENDPOINT_DETECTION_RESPONSE" }
      ]
    }
  ]
}

Somente o papel COMPANION_APP é compatível com extraCapabilities. Para permitir que o app complementar limpe o dispositivo ou o perfil de trabalho localmente, defina extraCapabilities.localDeviceWipeCommand como LOCAL_DEVICE_WIPE_COMMAND_ALLOWED para essa função.

Antes de atribuir os papéis conforme especificado na política, o sistema vai verificar se a impressão digital do certificado da chave de assinatura do app no dispositivo corresponde à da Google Play Store. Se a impressão digital for diferente, os papéis não serão atribuídos ao app, e a não conformidade com NonComplianceReason.APP_SIGNING_CERT_MISMATCH será informada ao EMM.

{
  "applications": [

    {
      "packageName": "com.example.mtd",
      "installType": "FORCE_INSTALLED",
      "signingKeyCerts": [
         { "signingKeyCertFingerprintSha256": "base64-encoded-sha256" }
       ],
      "roles": [
        { "roleType": "MOBILE_THREAT_DEFENSE_ENDPOINT_DETECTION_RESPONSE" }
      ]
    }
  ]
}

Se o app tiver uma configuração gerenciada, o admin de TI poderá definir uma configuração inicial para os restrictions relevantes na política do aplicativo:

{
  "applications": [

    {
      "packageName": "com.example.mtd",
      "installType": "FORCE_INSTALLED",
      "roles": [
        { "roleType": "MOBILE_THREAT_DEFENSE_ENDPOINT_DETECTION_RESPONSE" }
      ],
      "managedConfiguration": {
        "<key>": "<value>"
      }
    }
  ]
}

Verificar o status de isenção da bateria

Os apps com a função de defesa contra ameaças para dispositivos móveis (MTD, na sigla em inglês) são automaticamente isentos das restrições de energia padrão em segundo plano. No entanto, essa isenção é implementada pelo bucket do App em espera, e não pela lista de permissões de energia do sistema.

Para verificar se a isenção do app está ativa:

import android.app.usage.UsageStatsManager
import android.content.Context

// UsageStatsManager.STANDBY_BUCKET_EXEMPTED is annotated as a @SystemApi. We can redefine it for clarity.
// Define this at the top level of your file, or inside a companion object
private const val STANDBY_BUCKET_EXEMPTED = 5

private fun isAppExemptedFromAppStandbyBucket(): Boolean =
    (getSystemService(Context.USAGE_STATS_SERVICE) as? UsageStatsManager)
        ?.appStandbyBucket == STANDBY_BUCKET_EXEMPTED