Pakiet SDK interfejsu Android Management API (AMAPI) umożliwia aplikacji rozszerzenia określonej przez EMM bezpośrednią komunikację z Android Device Policy (ADP) i wykonywanie Commands na urządzeniu.
Więcej informacji o tej bibliotece i sposobie dodawania jej do aplikacji znajdziesz w artykule Integracja z pakietem AMAPI SDK.
Po zintegrowaniu pakietu SDK aplikacja rozszerzenia może komunikować się z ADP, aby:
- wysyłać żądania poleceń
- sprawdzać stan żądań poleceń,
- otrzymywać informacje o zmianach stanu poleceń,
Wydaj polecenie
Aplikacja rozszerzenia może żądać wydawania poleceń za pomocą ADP.
IssueCommandRequest zawiera obiekt żądania, który będzie zawierać szczegółowe informacje o poleceniu do wydania i konkretnych parametrach.
Wyczyść dane aplikacji
Poniższy fragment kodu pokazuje, jak wysłać żądanie wyczyszczenia danych pakietu:
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();
}
...
Wcześniejszy przykład pokazuje, jak wysłać żądanie wyczyszczenia danych aplikacji dla określonych pakietów i poczekać na pomyślne wykonanie polecenia. Jeśli polecenie zostanie wydane, zwrócony zostanie obiekt Command z bieżącym stanem polecenia i identyfikatorem polecenia, którego można później użyć do sprawdzenia stanu dowolnych długo działających poleceń.
Wyczyść urządzenie lub profil służbowy
Aplikacja towarzysząca może wydać lokalne polecenie Wipe, gdy administrator IT przypisze rolę COMPANION_APP z dodatkową funkcją LOCAL_DEVICE_WIPE_COMMAND_ALLOWED. Na urządzeniach należących do firmy przywraca ustawienia fabryczne, a na urządzeniach należących do użytkowników z profilem służbowym usuwa profil służbowy. Jeśli aplikacja nie ma tej roli i dodatkowych uprawnień, żądanie się nie powiedzie. Więcej informacji znajdziesz w artykule o konfigurowaniu zasad.
Oba parametry Wipe są opcjonalne. Jeśli nie chcesz podawać wartości, pomiń metodę ustawiającą kreatora lub przekaż pustą kolekcję albo pusty ciąg znaków (nie przekazuj null).
WipeDataFlagWartości określają, które dane są usuwane:PRESERVE_RESET_PROTECTION_DATA: zachowuje dane ochrony przed przywróceniem do ustawień fabrycznych, które są domyślnie usuwane. Dotyczy tylko urządzeń należących do firmy.WIPE_EXTERNAL_STORAGE: powoduje też wyczyszczenie pamięci zewnętrznej, np. kart SD. Dotyczy tylko urządzeń należących do firmy.WIPE_ESIMS: usuwa wszystkie karty eSIM z urządzeń należących do firmy. Na urządzeniach należących do użytkowników usuwa tylko zarządzane karty eSIM (na Androidzie 16 lub nowszym zarządzane karty eSIM są zawsze usuwane wraz z profilem służbowym, niezależnie od tego, czy ta flaga jest ustawiona).
wipeReason: krótka wiadomość (do 200 znaków), która wyświetla się użytkownikowi, gdy profil służbowy zostanie usunięty z urządzenia należącego do użytkownika. Nie ma ona wpływu na urządzenia należące do firmy.
Poniższy fragment kodu pokazuje, jak wysłać żądanie wyczyszczenia urządzenia lub profilu służbowego:
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();
}
...
Pobierz polecenie
Aplikacja rozszerzenia może sprawdzać stan wcześniej wysłanych żądań poleceń. Aby pobrać stan polecenia, potrzebujesz jego identyfikatora (dostępnego w żądaniu wydania polecenia). Poniższy fragment kodu pokazuje, jak wysłać GetCommandRequest do 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());
}
...
Nasłuchiwanie wywołań zwrotnych dotyczących zmiany stanu polecenia
Aplikacja rozszerzenia może opcjonalnie zarejestrować wywołanie zwrotne, aby otrzymywać aktualizacje dotyczące zmian stanu długotrwałych poleceń. Aby to zrobić, wykonaj te czynności:
- Zmiany stanu polecenia są przekazywane do
CommandListener. Zaimplementuj ten interfejs w swojej aplikacji i określ, jak mają być obsługiwane otrzymane aktualizacje stanu. - Rozszerz
NotificationReceiverServicei podaj instancjęCommandListenerza pomocą metodygetCommandListener. Skonfiguruj zasady aplikacji, aby przypisać do niej rolę
COMPANION_APP(patrz Konfiguracja zasad).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 ...; } }
Konfiguracja zasad
Aby umożliwić aplikacji towarzyszącej bezpośrednią komunikację z ADP, dostawca usług EMM musi przypisać jej rolę COMPANION_APP, używając pola roles w zasadach dotyczących aplikacji:
"applications": [{
"packageName": "com.amapi.extensibility.demo",
"installType": "FORCE_INSTALLED",
"roles": [
{ "roleType": "COMPANION_APP" }
]
}]
Aby zezwolić aplikacji towarzyszącej na wydawanie lokalnego polecenia Wipe, ustaw extraCapabilities.localDeviceWipeCommand na 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"
}
}
]
}]
Więcej informacji o dostępnych opcjach znajdziesz w artykule Konfigurowanie urządzenia za pomocą zasad ról aplikacji.
Usługa, która nasłuchuje zmian stanu poleceń, jest automatycznie wykrywana przez ADP za pomocą metadanych w pliku manifestu aplikacji i nie musi już być określana w zasadach jako notificationReceiver. Szczegółowe informacje o przygotowywaniu aplikacji i pliku manifestu znajdziesz w artykule Zarządzanie rolami aplikacji.
Testowanie
Testy jednostkowe
LocalCommandClient to interfejs, który umożliwia udostępnienie implementacji do testowania.
Testy integracji
Do przeprowadzenia testu z ADP potrzebne będą te informacje:
- Nazwa pakietu aplikacji rozszerzenia.
- Zakodowany w formacie base64 identyfikator SHA-256 podpisu powiązanego z pakietem aplikacji.
- Opcjonalnie, jeśli testujesz wywołanie zwrotne – pełna i jednoznaczna nazwa usługi z nowo wprowadzonej usługi obsługującej wywołanie zwrotne. (pełna i jednoznaczna nazwa
CommandServicew przykładzie).