Dokumen ini harus menjadi panduan utama Anda dalam menggunakan AMAPI SDK untuk tujuan menerima sinyal kepercayaan perangkat.
AMAPI SDK memungkinkan aplikasi Anda mengakses sinyal kepercayaan perangkat dari aplikasi Kebijakan Perangkat Android (ADP). Aplikasi Anda kemudian dapat menggunakan sinyal ini untuk menghitung status kepercayaan perangkat dan menerapkan logika bisnis sesuai pilihan.
Prasyarat
- Aplikasi Anda harus diizinkan untuk membaca sinyal kepercayaan perangkat:
- Aplikasi pendamping di perangkat yang dikelola AMAPI: Aplikasi yang diberi peran
COMPANION_APPdalam kebijakan dapat mengakses sinyal kepercayaan perangkat tanpa berada dalam daftar yang diizinkan di perangkat terkelola sepenuhnya atau dari dalam profil kerja (dengan akses terbatas ke sinyal dari profil kerja). Untuk mengetahui detailnya, lihat Mengelola peran aplikasi, Konfigurasi Kebijakan, dan Mengakses sinyal dari aplikasi pendamping. - Semua aplikasi dan lingkungan lainnya: Mengakses sinyal kepercayaan perangkat
dari dalam profil pribadi, di perangkat yang tidak dikelola, di perangkat
yang dikelola oleh DPC kustom, atau tanpa peran
COMPANION_APPmengharuskan aplikasi Anda berada dalam daftar yang diizinkan. Untuk mengetahui informasi tentang cara mendaftar, buka halaman akses sinyal kepercayaan perangkat.
- Aplikasi pendamping di perangkat yang dikelola AMAPI: Aplikasi yang diberi peran
- Android Enterprise merekomendasikan untuk mengintegrasikan rangkaian API Play Integrity ke dalam aplikasi klien Anda dan merujuk ke hasilnya sebelum membaca dan mengandalkan sinyal kepercayaan perangkat. Perangkat yang gagal dalam pemeriksaan Play Integrity API tidak boleh dipercaya, begitu juga dengan sinyal apa pun yang berasal dari perangkat yang digunakan untuk menentukan postur kepercayaan. Anda dapat membaca dokumentasi Play Integrity untuk mengetahui detail selengkapnya.
Mengintegrasikan dengan AMAPI SDK di aplikasi Anda
Untuk mengakses sinyal kepercayaan perangkat, aplikasi Anda harus terintegrasi dengan AMAPI SDK. Anda dapat menemukan informasi selengkapnya tentang library ini dan cara menambahkannya ke aplikasi Anda di panduan integrasi AMAPI SDK.
Menambahkan izin yang diperlukan
Beberapa sinyal yang ditampilkan dari Device Trust from Android Enterprise API mengharuskan aplikasi mendeklarasikan izin yang sama dengan yang diperlukan untuk mengakses informasi ini sejak awal, khususnya:
| Sinyal | Izin yang diperlukan |
|---|---|
| Status Jaringan | ACCESS_NETWORK_STATE |
| Kompleksitas kunci layar | REQUEST_PASSWORD_COMPLEXITY |
Jika izin ini tidak disertakan dalam AndroidManifest.xml aplikasi, Device Trust from Android Enterprise API akan menampilkan PERMISSION_ISSUE dalam metadata sinyal terkait:
internalDeviceSettings=DeviceSettings{
screenLockComplexity=COMPLEXITY_UNSPECIFIED,
internalScreenLockComplexityMetadata=Metadata{
dataIssues=[
DataIssue{
issueType=PERMISSION_ISSUE,
issueLevel=WARNING,
issueDetails=IssueDetailsCase{none}
}
]
},
Untuk mengetahui detail tambahan, lihat daftar sinyal kepercayaan perangkat yang tersedia.
Langkah-langkah untuk mengakses sinyal kepercayaan perangkat
Aplikasi dalam daftar yang diizinkan (termasuk aplikasi yang berjalan di dalam profil pribadi atau di perangkat yang tidak dikelola) harus memverifikasi bahwa lingkungan klien sudah yang terbaru dan menyiapkannya jika perlu sebelum meminta sinyal.
Langkah-langkah untuk aplikasi dalam daftar yang diizinkan untuk mengakses sinyal kepercayaan perangkat adalah:

Memverifikasi lingkungan klien
Contoh kode berikut menunjukkan cara menggunakan getEnvironment untuk membaca
status aplikasi ADP saat ini. Aplikasi Anda kemudian dapat menggunakan instance
deviceClient yang di-cache untuk mengakses sinyal kepercayaan perangkat jika lingkungan
sudah siap dan terbaru (lihat Mengakses sinyal kepercayaan perangkat).
Kotlin
import com.google.android.managementapi.common.model.Role import com.google.android.managementapi.device.DeviceClient import com.google.android.managementapi.device.DeviceClientFactory import com.google.android.managementapi.device.model.GetDeviceRequest import com.google.android.managementapi.environment.EnvironmentClient import com.google.android.managementapi.environment.EnvironmentClientFactory import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.INSTALLED import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.NOT_INSTALLED import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE import com.google.android.managementapi.environment.model.GetEnvironmentRequest import com.google.android.managementapi.environment.model.PrepareEnvironmentRequest try { val context = applicationContext val roles = listOf(Role.builder().setRoleType(Role.RoleType.IDENTITY_PROVIDER).build()) val request = GetEnvironmentRequest.builder().setRoles(roles).build() val environmentClient = EnvironmentClientFactory.create(context) val environmentResponse = environmentClient.getEnvironment(request) if (environmentResponse.hasAndroidDevicePolicyEnvironment()) { val adpEnvironment = environmentResponse.androidDevicePolicyEnvironment if (adpEnvironment.state == READY && adpEnvironment.version == UP_TO_DATE) { // AMAPI Environment State OK, Version OK. Requesting Device signals.. checkDevice(deviceClient) } else if (adpEnvironment.state == INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment won't show the UI prepareEnvironment(context, environmentClient) } else if (adpEnvironment.state == NOT_INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment will show the UI prepareEnvironment(context, environmentClient) } } } catch (e: Exception) { Log.e(TAG, "Exception", e) }
Java
import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.INSTALLED; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.NOT_INSTALLED; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE; import com.google.android.managementapi.common.model.Role; import com.google.android.managementapi.device.DeviceClient; import com.google.android.managementapi.device.DeviceClientFactory; import com.google.android.managementapi.device.model.Device; import com.google.android.managementapi.device.model.GetDeviceRequest; import com.google.android.managementapi.environment.EnvironmentClient; import com.google.android.managementapi.environment.EnvironmentClientFactory; import com.google.android.managementapi.environment.model.Environment; import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment; import com.google.android.managementapi.environment.model.GetEnvironmentRequest; import com.google.android.managementapi.environment.model.PrepareEnvironmentRequest; import com.google.android.managementapi.environment.model.PrepareEnvironmentResponse; try { Context context = getApplicationContext(); ImmutableListroles = new ImmutableList.Builder () .add(Role.builder() .setRoleType(Role.RoleType.IDENTITY_PROVIDER) .build()) .build(); EnvironmentClient environmentClient = EnvironmentClientFactory.create(context); GetEnvironmentRequest request = GetEnvironmentRequest.builder() .setRoles(roles) .build(); ListenableFuture getEnvironmentFuture = environmentClient.getEnvironmentAsync(request); Futures.addCallback(getEnvironmentFuture, new FutureCallback<>() { @Override public void onSuccess(Environment environment) { AndroidDevicePolicyEnvironment adpEnvironment = environment.getAndroidDevicePolicyEnvironment(); AndroidDevicePolicyEnvironment.State state = adpEnvironment.getState(); AndroidDevicePolicyEnvironment.Version version = adpEnvironment.getVersion(); if (state == READY && version == UP_TO_DATE) { // AMAPI Environment State OK, Version OK. Requesting Device signals.. checkDevice(deviceClient); } else if (state == INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment won't show the UI prepareEnvironment(context, environmentClient); } else if (state == NOT_INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment will show the UI prepareEnvironment(context, environmentClient); } } @Override public void onFailure(Throwable t) { Log.d(TAG, t.toString()); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.d(TAG, e.toString()); }
Jika aplikasi ADP diinstal, tetapi tidak diupdate, aplikasi Anda harus memanggil
prepareEnvironment untuk mengupdate aplikasi ADP secara otomatis tanpa intervensi pengguna.
Jika aplikasi ADP tidak diinstal, aplikasi Anda dapat memanggil prepareEnvironment
untuk meminta pengguna menginstal aplikasi ADP. Lihat
Menyiapkan lingkungan klien.
Menyiapkan lingkungan klien
Jika aplikasi ADP sudah diinstal, API akan mengupdatenya secara otomatis tanpa intervensi pengguna.
Jika aplikasi ADP tidak diinstal, API akan meminta pengguna untuk menyetujui penginstalan aplikasi ADP.
Anda dapat mendaftarkan callback untuk memantau pilihan pengguna. Lihat Melacak interaksi pengguna selama penginstalan aplikasi ADP untuk mengetahui detail tambahan.
Sebaiknya panggilan prepareEnvironment dilakukan dari proses latar depan, selama alur UX orientasi agar pengguna tidak terkejut dengan
dialog modal Instal Android Device Policy.
Jika panggilan dari proses latar depan tidak memungkinkan, karena ini adalah alur Web
dan komponen Android tidak memiliki UI, panggilan dari latar belakang diizinkan dengan
persyaratan bahwa hal ini terjadi selama alur UX orientasi.
Setelah lingkungan disiapkan dengan benar, Anda dapat mengakses Sinyal kepercayaan perangkat. Lihat Mengakses sinyal kepercayaan perangkat.
Kotlin
try { val myNotificationReceiverService = ComponentName( context, MyNotificationReceiverService::class.java ) val roles = listOf(Role.builder().setRoleType(Role.RoleType.IDENTITY_PROVIDER).build()) val request = PrepareEnvironmentRequest.builder().setRoles(roles).build() val response = environmentClient.prepareEnvironment(request, myNotificationReceiverService) val environment = response.environment val adpEnvironment = environment.androidDevicePolicyEnvironment val state = adpEnvironment.state val version = adpEnvironment.version if (state == READY && version == UP_TO_DATE) { // Environment is prepared, access device posture signals using // DeviceClient. checkDevice(deviceClient) } else { // The prepareEnvironment call failed to prepare Log.w( TAG, "AMAPI environment was not ready: " + state + " - " + version ) } } catch (e: java.lang.Exception) { Log.d(TAG, e.toString()) }
Java
try { ComponentName myNotificationReceiverService = new ComponentName( context, MyNotificationReceiverService.class ); ImmutableListroles = new ImmutableList.Builder () .add(Role.builder() .setRoleType(Role.RoleType.IDENTITY_PROVIDER) .build()) .build(); PrepareEnvironmentRequest request = PrepareEnvironmentRequest.builder() .setRoles(roles) .build(); ListenableFuture environmentFuture = environmentClient.prepareEnvironmentAsync( request, myNotificationReceiverService ); Futures.addCallback(environmentFuture, new FutureCallback<>() { @Override public void onSuccess(PrepareEnvironmentResponse response) { Environment environment = response.getEnvironment(); AndroidDevicePolicyEnvironment adpEnvironment = environment.getAndroidDevicePolicyEnvironment(); AndroidDevicePolicyEnvironment.State state = adpEnvironment.getState(); AndroidDevicePolicyEnvironment.Version version = adpEnvironment.getVersion(); if (state == READY && version == UP_TO_DATE) { // AMAPI Environment State OK, Version OK. Requesting Device signals.. checkDevice(deviceClient); } else { // The prepareEnvironment call failed to prepare Log.w( TAG, "AMAPI environment was not ready: " + adpEnvironment.getState() + " - " + adpEnvironment.getVersion() ); } } @Override public void onFailure(@NonNull Throwable t) { // Handle the error Log.d(TAG, "AMAPI response did not contain an ADP environment"); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.d(TAG, e.toString()); }
Mengakses sinyal kepercayaan perangkat
Rekomendasi penggunaan DeviceClient
Membuat instance deviceClient (atau instance EnvironmentClient) akan membuat saluran komunikasi dengan aplikasi Kebijakan Perangkat Android (ADP) yang otomatis melepaskan binding saat tidak ada aktivitas. Karena instance klien tidak mengekspos metode penonaktifan eksplisit, membuat dan menghapus beberapa instance akan menyisakan saluran tanpa induk untuk pembersihan sampah memori.
Untuk mengoptimalkan performa dan menghindari saluran tanpa induk, buat satu instance
DeviceClient di cakupan aplikasi menggunakan
DeviceClientFactory.create(context.applicationContext) dan simpan dalam cache untuk digunakan kembali
di seluruh aplikasi Anda—misalnya, sebagai singleton cakupan aplikasi atau
menggunakan injeksi dependensi.
Untuk mengakses sinyal kepercayaan perangkat yang Anda minati, gunakan instance
deviceClient yang di-cache untuk meminta objek Device:
Kotlin
runCatching { deviceClient.getDeviceAwait(GetDeviceRequest.getDefaultInstance()) }.onFailure { t -> Log.d(TAG, t.toString()) }.onSuccess { device -> // Access device posture signals available in device val deviceString = device.toString() Log.d(TAG, deviceString) }
Java
ListenableFuturedeviceFuture = deviceClient.getDevice(GetDeviceRequest.getDefaultInstance()); Futures.addCallback(deviceFuture, new FutureCallback () { @Override public void onSuccess(Device device) { // Access device posture signals available in device String deviceString = device.toString(); Log.d(TAG, deviceString); } @Override public void onFailure(Throwable t) { Log.d(TAG, t.toString()); } }, MoreExecutors.directExecutor());
Mengakses sinyal dari aplikasi pendamping
Di perangkat terkelola sepenuhnya atau dari dalam profil kerja yang dikelola dengan
Android Management API (AMAPI), aplikasi yang diberi
peran COMPANION_APP dapat membaca sinyal kepercayaan perangkat tanpa
terdaftar di daftar yang diizinkan.
Saat dipanggil dari dalam profil kerja (baik di perangkat milik perusahaan maupun milik pribadi), aplikasi Anda harus berjalan di dalam profil kerja, dan akses dibatasi ke sinyal dari profil kerja (misalnya, status pengelolaan, kompleksitas kunci layar, dan aplikasi yang diinstal hanya mencerminkan profil kerja).
1. Menetapkan peran COMPANION_APP dalam kebijakan
Konfigurasikan kebijakan EMM Anda untuk memberikan roleType: "COMPANION_APP" ke
aplikasi Anda dalam daftar kebijakan applications:
- Untuk mengonfigurasi manifes aplikasi Anda dan secara opsional memproses penetapan peran, lihat Mengelola peran aplikasi.
- Untuk sintaksis konfigurasi kebijakan, lihat Konfigurasi Kebijakan.
2. Membaca sinyal langsung dengan DeviceClient
Buat dan simpan satu instance deviceClient dalam cache pada cakupan aplikasi menggunakan
DeviceClientFactory.create (lihat
Rekomendasi penggunaan DeviceClient), lalu
minta objek Device secara langsung:
Kotlin
import android.content.Context import android.util.Log import com.google.android.managementapi.device.DeviceClient import com.google.android.managementapi.device.DeviceClientFactory import com.google.android.managementapi.device.model.GetDeviceRequest // Instantiate once at application scope (for example, using dependency injection) // to reuse a single DeviceClient instance across your app. class DeviceTrustRepository(context: Context) { private val deviceClient: DeviceClient = DeviceClientFactory.create(context.applicationContext) suspend fun readSignalsFromCompanionApp() { runCatching { deviceClient.getDeviceAwait(GetDeviceRequest.getDefaultInstance()) }.onSuccess { device -> Log.d(TAG, "Device signals: $device") }.onFailure { t -> Log.e(TAG, "Failed to read device signals", t) } } companion object { private const val TAG = "DeviceTrustRepository" } }
Java
import android.content.Context; import android.util.Log; import com.google.android.managementapi.device.DeviceClient; import com.google.android.managementapi.device.DeviceClientFactory; import com.google.android.managementapi.device.model.Device; import com.google.android.managementapi.device.model.GetDeviceRequest; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; // Instantiate once at application scope (for example, using dependency injection) // to reuse a single DeviceClient instance across your app. public class DeviceTrustRepository { private static final String TAG = "DeviceTrustRepository"; private final DeviceClient deviceClient; public DeviceTrustRepository(Context context) { this.deviceClient = DeviceClientFactory.create(context.getApplicationContext()); } public void readSignalsFromCompanionApp() { ListenableFuturedeviceFuture = deviceClient.getDevice(GetDeviceRequest.getDefaultInstance()); Futures.addCallback(deviceFuture, new FutureCallback () { @Override public void onSuccess(Device device) { Log.d(TAG, "Device signals: " + device); } @Override public void onFailure(Throwable t) { Log.e(TAG, "Failed to read device signals", t); } }, MoreExecutors.directExecutor()); } }
Menangani error
Jika aplikasi Anda membuat permintaan kepercayaan perangkat dan panggilan gagal, aplikasi Anda akan menerima pengecualian. Pengecualian ini dapat terjadi karena berbagai alasan, seperti:
SecurityException- aplikasi Anda tidak diizinkan untuk membuat permintaan (misalnya, aplikasi Anda tidak terdaftar di daftar yang diizinkan saat diperlukan, tidak diberi peranCOMPANION_APPdi perangkat yang dikelola AMAPI, atau memanggilprepareEnvironmentsebagai aplikasi pendamping yang tidak ada dalam daftar yang diizinkan). Lihat akses sinyal kepercayaan perangkat dan Akses sinyal dari aplikasi pendamping.- Subclass
AmapiSdkException- ADP tidak ada atau belum siap. Hal ini dapat disebabkan oleh kondisi sementara dan Anda harus mencoba kembali panggilan tersebut.
Strategi percobaan ulang
Untuk pengecualian karena kondisi sementara, terapkan strategi percobaan ulang dengan backoff eksponensial. Setelah kegagalan pertama, mulailah dengan penundaan awal selama 5 detik sebelum mencoba lagi.
Terapkan strategi percobaan ulang dengan jumlah maksimum upaya sebagai kondisi keluar menggunakan penundaan yang meningkat secara eksponensial setiap kali (10 dtk, 20 dtk, dll.).
Melacak interaksi pengguna selama penginstalan aplikasi ADP
Jika perangkat perlu menginstal aplikasi ADP selama prepareEnvironment, aplikasi Anda dapat melacak interaksi pengguna yang menerapkan NotificationReceiverService untuk menerima notifikasi yang menggantikan getPrepareEnvironmentListener:
Kotlin
import android.util.Log import com.google.android.managementapi.environment.EnvironmentListener import com.google.android.managementapi.environment.model.EnvironmentEvent.EventCase.Kind.ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED import com.google.android.managementapi.environment.model.EnvironmentEvent import com.google.android.managementapi.notification.NotificationReceiverService class MyNotificationReceiverService : NotificationReceiverService() { override fun getPrepareEnvironmentListener(): EnvironmentListener { return MyEnvironmentListener() } } class MyEnvironmentListener : EnvironmentListener { override fun onEnvironmentEvent( event: EnvironmentEvent ) { if (event.event.kind == ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED) { Log.d(TAG, "User provided install consent") } else { Log.d(TAG, "User rejected install consent") } } companion object { private val TAG: String = MyEnvironmentListener::class.java.simpleName } }
Java
import static com.google.android.managementapi.environment.model.EnvironmentEvent.EventCase.Kind.ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED; import android.util.Log; import androidx.annotation.NonNull; import com.google.android.managementapi.environment.EnvironmentListener; import com.google.android.managementapi.environment.model.EnvironmentEvent; import com.google.android.managementapi.notification.NotificationReceiverService; class MyNotificationReceiverService extends NotificationReceiverService { @NonNull @Override protected EnvironmentListener getPrepareEnvironmentListener() { return new MyEnvironmentListener(); } } class MyEnvironmentListener implements EnvironmentListener { final private String TAG = MyEnvironmentListener.class.getSimpleName(); @Override public void onEnvironmentEvent(EnvironmentEvent event) { if (event.getEvent().getKind() == ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED) { Log.d(TAG, "User provided install consent"); } else { Log.d(TAG, "User rejected install consent"); } } }
Masalah umum
Saat ini tidak ada masalah umum.