SDK Android Management API (AMAPI) cho phép một ứng dụng tiện ích do EMM chỉ định
giao tiếp trực tiếp với Android Device Policy (ADP) và thực thi Commands
trên thiết bị.
Tích hợp với AMAPI SDK cung cấp thêm thông tin về thư viện này và cách thêm thư viện vào ứng dụng của bạn.
Sau khi tích hợp SDK, ứng dụng tiện ích của bạn có thể giao tiếp với ADP để:
Ra lệnh
Ứng dụng tiện ích có thể yêu cầu phát lệnh bằng ADP.
IssueCommandRequest chứa đối tượng yêu cầu sẽ chứa thông tin chi tiết về lệnh được đưa ra và các tham số cụ thể.
Xoá dữ liệu ứng dụng
Đoạn mã sau đây cho biết cách đưa ra yêu cầu xoá dữ liệu của gói:
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();
}
...
Ví dụ trước cho thấy việc đưa ra yêu cầu xoá dữ liệu ứng dụng cho các gói được chỉ định và chờ cho đến khi lệnh được đưa ra thành công. Nếu
được phát hành thành công, đối tượng Command sẽ được trả về cùng với
trạng thái lệnh hiện tại và mã lệnh. Sau này, bạn có thể dùng mã lệnh này để truy vấn trạng thái của
bất kỳ lệnh nào chạy trong thời gian dài.
Xoá thiết bị hoặc hồ sơ công việc
Ứng dụng đồng hành có thể đưa ra lệnh Wipe cục bộ khi quản trị viên CNTT chỉ định vai trò COMPANION_APP có chức năng bổ sung LOCAL_DEVICE_WIPE_COMMAND_ALLOWED. Trên thiết bị do công ty sở hữu, thao tác này sẽ đặt lại thiết bị về trạng thái ban đầu; trên thiết bị thuộc sở hữu cá nhân có hồ sơ công việc, thao tác này sẽ xoá hồ sơ công việc. Nếu ứng dụng không có vai trò và chức năng bổ sung này, thì yêu cầu sẽ không thành công. Để biết thông tin chi tiết, hãy xem phần Cấu hình chính sách.
Cả hai tham số Wipe đều không bắt buộc. Nếu bạn không muốn chỉ định một giá trị, hãy bỏ qua phương thức setter của trình tạo hoặc truyền một tập hợp trống hoặc chuỗi trống (không truyền null).
- Giá trị
WipeDataFlagkiểm soát dữ liệu nào bị xoá:PRESERVE_RESET_PROTECTION_DATA: Giữ lại dữ liệu bảo vệ khi đặt lại về trạng thái ban đầu (dữ liệu này sẽ bị xoá theo mặc định). Chỉ áp dụng cho các thiết bị do công ty sở hữu.WIPE_EXTERNAL_STORAGE: Đồng thời xoá sạch bộ nhớ ngoài, chẳng hạn như thẻ SD. Chỉ áp dụng cho các thiết bị do công ty sở hữu.WIPE_ESIMS: Xoá tất cả eSIM khỏi thiết bị do công ty sở hữu. Trên thiết bị thuộc sở hữu cá nhân, chỉ xoá eSIM được quản lý (trên Android 16 trở lên, eSIM được quản lý luôn bị xoá cùng với hồ sơ công việc, bất kể cờ này có được đặt hay không).
wipeReason: Một thông báo ngắn, tối đa 200 ký tự, hiển thị cho người dùng khi hồ sơ công việc bị xoá khỏi thiết bị thuộc sở hữu cá nhân. Chế độ này không ảnh hưởng đến các thiết bị do công ty sở hữu.
Đoạn mã sau đây cho biết cách đưa ra yêu cầu xoá sạch thiết bị hoặc hồ sơ công việc:
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();
}
...
Nhận lệnh
Ứng dụng tiện ích có thể truy vấn trạng thái của các yêu cầu lệnh đã đưa ra trước đó. Để truy xuất trạng thái của lệnh, bạn cần có mã lệnh (có trong yêu cầu phát lệnh). Đoạn mã sau đây cho biết cách gửi
GetCommandRequest đến 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());
}
...
Lắng nghe lệnh gọi lại thay đổi trạng thái lệnh
Ứng dụng tiện ích có thể tuỳ ý đăng ký một lệnh gọi lại để nhận thông tin cập nhật về các thay đổi trạng thái của lệnh chạy trong thời gian dài theo các bước sau:
- Các thay đổi về trạng thái lệnh được thông báo cho
CommandListener, hãy triển khai giao diện này trong ứng dụng của bạn và cung cấp cách triển khai về cách xử lý các bản cập nhật trạng thái nhận được. - Mở rộng
NotificationReceiverServicevà cung cấp một thực thểCommandListenerthông qua phương thứcgetCommandListener. Định cấu hình chính sách ứng dụng để chỉ định vai trò
COMPANION_APPcho ứng dụng của bạn (xem phần Cấu hình chính sách).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 ...; } }
Cấu hình chính sách
Để cho phép ứng dụng đồng hành giao tiếp trực tiếp với ADP, EMM phải chỉ định vai trò COMPANION_APP cho ứng dụng bằng cách sử dụng trường roles trong chính sách ứng dụng:
"applications": [{
"packageName": "com.amapi.extensibility.demo",
"installType": "FORCE_INSTALLED",
"roles": [
{ "roleType": "COMPANION_APP" }
]
}]
Để cho phép ứng dụng đồng hành đưa ra lệnh Wipe cục bộ, hãy đặt extraCapabilities.localDeviceWipeCommand thành 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"
}
}
]
}]
Để biết các lựa chọn khác, hãy xem phần Cấp phép thiết bị bằng chính sách về vai trò của ứng dụng.
Dịch vụ theo dõi các thay đổi về trạng thái lệnh sẽ được ADP tự động phát hiện bằng cách sử dụng siêu dữ liệu trong tệp kê khai của ứng dụng và không cần phải được chỉ định trong chính sách dưới dạng notificationReceiver nữa. Để biết thông tin chi tiết về cách chuẩn bị ứng dụng và tệp kê khai, hãy xem bài viết Quản lý vai trò của ứng dụng.
Thử nghiệm
Kiểm thử đơn vị
LocalCommandClient là một giao diện, do đó cho phép cung cấp một phương thức triển khai có thể kiểm thử.
Kiểm thử tích hợp
Bạn cần có những thông tin sau để thử nghiệm với ADP:
- Tên gói của ứng dụng tiện ích.
- Hàm băm SHA-256 được mã hoá theo chuẩn base64 của Chữ ký liên kết với gói ứng dụng.
- Nếu bạn muốn kiểm thử lệnh gọi lại, hãy cung cấp tên đủ điều kiện của dịch vụ từ dịch vụ mới được giới thiệu để hỗ trợ lệnh gọi lại. (Tên đủ điều kiện của
CommandServicetrong ví dụ).