El SDK de la API de Android Management (AMAPI) permite que una app de extensión especificada por el EMM
se comunique directamente con Android Device Policy (ADP) y ejecute Commands
en el dispositivo.
En Cómo realizar la integración con el SDK de AMAPI, se proporciona más información sobre esta biblioteca y cómo agregarla a tu aplicación.
Una vez que se integra el SDK, tu app de extensión puede comunicarse con ADP para lo siguiente:
- enviar solicitudes de comando
- consultar el estado de las solicitudes de comandos
- recibir cambios de estado de los comandos
Emisión de comandos
Una app de extensión puede solicitar que se emitan comandos con ADP.
IssueCommandRequest contiene el objeto de solicitud que incluirá detalles sobre
el comando que se emitirá y los parámetros específicos.
Borra los datos de apps
En el siguiente fragmento, se muestra cómo emitir una solicitud para borrar los datos del paquete:
import android.util.Log;
...
import com.google.android.managementapi.commands.LocalCommandClientFactory;
import com.google.android.managementapi.commands.model.Command;
import com.google.android.managementapi.commands.model.GetCommandRequest;
import com.google.android.managementapi.commands.model.IssueCommandRequest;
import com.google.android.managementapi.commands.model.IssueCommandRequest.ClearAppsData;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
...
void issueClearAppDataCommand(ImmutableList<String> packageNames) {
Futures.addCallback(
LocalCommandClientFactory.create(getContext())
.issueCommand(createClearAppRequest(packageNames)),
new FutureCallback<Command>() {
@Override
public void onSuccess(Command result) {
// Process the returned command result here
Log.i(TAG, "Successfully issued command");
}
@Override
public void onFailure(Throwable t) {
Log.e(TAG, "Failed to issue command", t);
}
},
MoreExecutors.directExecutor());
}
IssueCommandRequest createClearAppRequest(ImmutableList<String> packageNames) {
return IssueCommandRequest.builder()
.setClearAppsData(
ClearAppsData.builder()
.setPackageNames(packageNames)
.build()
)
.build();
}
...
En el ejemplo anterior, se muestra cómo emitir una solicitud de borrado de datos de app para los paquetes especificados y esperar hasta que el comando se haya emitido correctamente. Si
se emite correctamente, se devolverá un objeto Command con el estado
actual del comando y el ID del comando, que se puede usar más adelante para consultar el estado de
cualquier comando de larga duración.
Borra el dispositivo o el perfil de trabajo
Una app complementaria puede emitir un comando Wipe local cuando el administrador de TI asigna
el rol COMPANION_APP con la capacidad adicional
LOCAL_DEVICE_WIPE_COMMAND_ALLOWED. En los dispositivos de la empresa, se restablece la configuración de fábrica; en los dispositivos personales con un perfil de trabajo, se quita el perfil de trabajo. Si
la app no tiene este rol ni la capacidad adicional, la solicitud falla. Para
obtener más detalles, consulta Configuración de la política.
Ambos parámetros Wipe son opcionales. Si no quieres especificar un valor, omite
el método set del compilador o pasa una colección vacía o una cadena vacía (no pases
null).
- Los valores
WipeDataFlagcontrolan qué datos se borran:PRESERVE_RESET_PROTECTION_DATA: Conserva los datos de la protección contra el restablecimiento de la configuración de fábrica, que se borran de forma predeterminada. Solo se aplica a los dispositivos de la empresa.WIPE_EXTERNAL_STORAGE: También borra el almacenamiento externo, como las tarjetas SD. Solo se aplica a los dispositivos de la empresa.WIPE_ESIMS: Quita todas las eSIM de los dispositivos de la empresa. En los dispositivos de propiedad personal, solo se quitan las eSIM administradas (en Android 16 o versiones posteriores, las eSIM administradas siempre se quitan con el perfil de trabajo, independientemente de si se establece este parámetro o no).
wipeReason: Es un mensaje breve, de hasta 200 caracteres, que se muestra al usuario cuando se quita el perfil de trabajo de un dispositivo personal. No tiene ningún efecto en los dispositivos de la empresa.
En el siguiente fragmento, se muestra cómo emitir una solicitud para borrar los datos del dispositivo o del perfil de trabajo:
Kotlin
import android.content.Context
import android.util.Log
...
import com.google.android.managementapi.commands.CommandException
import com.google.android.managementapi.commands.LocalCommandClientFactory
import com.google.android.managementapi.commands.model.IssueCommandRequest
import com.google.android.managementapi.commands.model.IssueCommandRequest.Wipe
import com.google.android.managementapi.commands.model.IssueCommandRequest.Wipe.WipeDataFlag
...
suspend fun issueWipeCommand(
context: Context,
wipeDataFlags: List<WipeDataFlag>,
wipeReason: String,
) {
try {
val command =
LocalCommandClientFactory.create(context)
.issueCommandAwait(createWipeRequest(wipeDataFlags, wipeReason))
Log.i(TAG, "Successfully issued wipe command: ${command.commandId}")
} catch (e: CommandException) {
Log.e(TAG, "Failed to issue wipe command", e)
}
}
fun createWipeRequest(
wipeDataFlags: List<WipeDataFlag>,
wipeReason: String,
): IssueCommandRequest =
IssueCommandRequest.builder()
.setWipe(
Wipe.builder()
.setWipeDataFlags(wipeDataFlags)
.setWipeReason(wipeReason)
.build()
)
.build()
...
Java
import android.content.Context;
import android.util.Log;
...
import com.google.android.managementapi.commands.LocalCommandClientFactory;
import com.google.android.managementapi.commands.model.Command;
import com.google.android.managementapi.commands.model.IssueCommandRequest;
import com.google.android.managementapi.commands.model.IssueCommandRequest.Wipe;
import com.google.android.managementapi.commands.model.IssueCommandRequest.Wipe.WipeDataFlag;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
...
void issueWipeCommand(
Context context,
ImmutableList<WipeDataFlag> wipeDataFlags,
String wipeReason) {
Futures.addCallback(
LocalCommandClientFactory.create(context)
.issueCommand(createWipeRequest(wipeDataFlags, wipeReason)),
new FutureCallback<Command>() {
@Override
public void onSuccess(Command result) {
Log.i(TAG, "Successfully issued wipe command: " + result.getCommandId());
}
@Override
public void onFailure(Throwable t) {
Log.e(TAG, "Failed to issue wipe command", t);
}
},
MoreExecutors.directExecutor());
}
IssueCommandRequest createWipeRequest(
ImmutableList<WipeDataFlag> wipeDataFlags, String wipeReason) {
return IssueCommandRequest.builder()
.setWipe(
Wipe.builder()
.setWipeDataFlags(wipeDataFlags)
.setWipeReason(wipeReason)
.build())
.build();
}
...
Obtener comando
Una app de extensión puede consultar el estado de las solicitudes de comandos emitidas anteriormente. Para
recuperar el estado de un comando, necesitarás el ID del comando (disponible en la
solicitud de emisión del comando). En el siguiente fragmento, se muestra cómo enviar un
GetCommandRequest a ADP.
import android.util.Log;
...
import com.google.android.managementapi.commands.LocalCommandClientFactory;
...
import com.google.android.managementapi.commands.model.GetCommandRequest;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
...
void getCommand(String commandId) {
Futures.addCallback(
LocalCommandClientFactory.create(getApplication())
.getCommand(GetCommandRequest.builder().setCommandId(commandId).build()),
new FutureCallback<Command>() {
@Override
public void onSuccess(Command result) {
// Process the returned command result here
Log.i(Constants.TAG, "Successfully issued command");
}
@Override
public void onFailure(Throwable t) {
Log.e(Constants.TAG, "Failed to issue command", t);
}
},
MoreExecutors.directExecutor());
}
...
Escucha las devoluciones de llamada de cambio de estado de Command
De manera opcional, una app de extensión puede registrar una devolución de llamada para recibir actualizaciones de los cambios de estado de los comandos de larga duración siguiendo estos pasos:
- Los cambios de estado de los comandos se notifican a
CommandListener. Implementa esta interfaz en tu app y proporciona una implementación sobre cómo controlar las actualizaciones de estado recibidas. - Extiende
NotificationReceiverServicey proporciona una instancia deCommandListenera través del métodogetCommandListener. Configura la política de la aplicación para asignar el rol
COMPANION_APPa tu app (consulta Configuración de la política).import com.google.android.managementapi.commands.CommandListener; import com.google.android.managementapi.notification.NotificationReceiverService; ... public class SampleCommandService extends NotificationReceiverService { @Override public CommandListener getCommandListener() { // return the concrete implementation from previous step return ...; } }
Configuración de la política
Para permitir que la app complementaria se comunique directamente con ADP, la EMM debe asignar el rol COMPANION_APP a la app con el campo roles en la política de la aplicación:
"applications": [{
"packageName": "com.amapi.extensibility.demo",
"installType": "FORCE_INSTALLED",
"roles": [
{ "roleType": "COMPANION_APP" }
]
}]
Para permitir que la app complementaria emita un comando Wipe local, establece extraCapabilities.localDeviceWipeCommand en LOCAL_DEVICE_WIPE_COMMAND_ALLOWED:
"applications": [{
"packageName": "com.amapi.extensibility.demo",
"installType": "FORCE_INSTALLED",
"roles": [
{
"roleType": "COMPANION_APP",
"extraCapabilities": {
"localDeviceWipeCommand": "LOCAL_DEVICE_WIPE_COMMAND_ALLOWED"
}
}
]
}]
Para conocer las opciones adicionales disponibles, consulta Aprovisiona el dispositivo con políticas de roles de la app.
ADP detecta automáticamente el servicio que escucha los cambios de estado de los comandos
con los metadatos del manifiesto de la app, y ya no es necesario
especificarlo en la política como notificationReceiver. Para obtener detalles sobre cómo
preparar tu app y el manifiesto, consulta Administra los roles de la aplicación.
Pruebas
Pruebas de unidades
LocalCommandClient es una interfaz y, por lo tanto, permite proporcionar una implementación comprobable.
Pruebas de integración
Para realizar pruebas con ADP, se necesitará la siguiente información:
- Es el nombre del paquete de la app de extensión.
- Es el hash SHA-256 codificado en Base64 de la firma asociada con el paquete de la app.
- De manera opcional, si se prueba la devolución de llamada, el nombre completamente calificado del servicio del servicio recién introducido para admitir la devolución de llamada. (nombre completamente calificado de
CommandServiceen el ejemplo).