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
enablede tenhameta-datacomandroid:nameigual acom.google.android.managementapi.notification.NotificationReceiverService.SERVICE_APP_ROLES. - Este serviço precisa ter
android:exporteddefinido comotrue - O
android:valuedometa-dataprecisa 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