The Android Management API (AMAPI) SDK enables an EMM-specified extension app to
communicate directly with Android Device Policy (ADP) and execute Commands
on the device.
Integrate with the AMAPI SDK provides more information about this library and how to add it to your application.
Once integrated the SDK, your extension app can communicate with ADP to:
Issue Command
An extension app can request for commands to be issued using ADP.
IssueCommandRequest
contains the request object that will contain detail on
the command to be issued and specific parameters.
The following snippet shows how to issue a request to clear the package's data:
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();
}
...
The earlier example shows issuing a clear app data request for specified
packages and waiting until the command has been successfully issued. If
successfully issued, a Command
object will be returned with the current
command status and the command ID which can later be used to query the status of
any long running commands.
Get Command
An extension app can query the status of previously issued command requests. To
retrieve a command's status, you will need the command ID (available from the
issue command request). The following snippet shows how to send a
GetCommandRequest
to 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());
}
...
Listen to Command status change callbacks
An extension app can register a callback to receive updates for status changes of long running commands following these steps:
- Command status changes are notified to
CommandListener
, implement this interface in your app and provide implementation on how to handle the received status updates. - Extend
NotificationReceiverService
and provideCommandListener
instance. Specify class name of extended
NotificationReceiverService
in Android Management API policy (see Policy Configuration).import com.google.android.managementapi.commands.CommandListener; import com.google.android.managementapi.notification.NotificationReceiverService; ... public class SampleCommandService extends NotificationReceiverService { @Override protected void setupInjection() { // (Optional) If using DI and needs initialisation then use this method. } @Override public CommandListener getCommandListener() { // return the concrete implementation from previous step return ...; } }
Add the service to your
AndroidManifest.xml
and ensure that it is exported.<service android:name = ".notification.SampleCommandService" android:exported = "true" />
Policy Configuration
To enable the extension app to communicate directly with ADP, the EMM has to
provide an extensionConfig
policy.
"applications": [{
"packageName": "com.amapi.extensibility.demo",
...
"extensionConfig": {
"signingKeyFingerprintsSha256": [
// Include signing key of extension app
],
// Optional if callback is implemented
"notificationReceiver": "com.amapi.extensibility.demo.notification.SampleCommandService"
}
}]
Testing
Unit testing
LocalCommandClient
is an interface and thus allows to provide a testable
implementation.
Integration testing
The following information will be needed to test with ADP:
- Package name of the extension app.
- The hex-encoded SHA-256 hash of the Signature associated with the app package.
- Optionally, if testing callback - fully qualified name of the service from
the newly introduced service to support callback. (Fully qualified name of
CommandService
in the example).