המסמך הזה הוא המדריך העיקרי לשימוש ב-AMAPI SDK לצורך קבלת אותות מהימנות של מכשירים.
ערכת ה-SDK של AMAPI מאפשרת לאפליקציה שלכם (שנקראת לפעמים אפליקציית 'לוויין') לגשת לאותות מהימנות המכשיר מאפליקציית ADP (Android Device Policy). לאחר מכן, האפליקציה יכולה להשתמש באותות האלה כדי לחשב את מצב המהימנות של המכשיר וליישם לוגיקה עסקית לפי בחירתכם.
דרישות מוקדמות
- הגישה לאותות האמינות של המכשיר מוגבלת כדי למנוע שימוש לא מורשה. מידע על אופן ההפעלה מופיע בדף גישה לאותות מהימנות של מכשירים.
- ב-Android Enterprise מומלץ לשלב את חבילת ממשקי Play Integrity API באפליקציית הלקוח, ולעיין בתוצאה לפני שקוראים את אותות המהימנות של המכשיר ומסתמכים עליהם. אסור לסמוך על מכשירים שנכשלים בבדיקות של Play Integrity API, וגם לא על אותות שמגיעים מהמכשיר ומשמשים לקביעת רמת האמון. פרטים נוספים מופיעים במאמרי העזרה בנושא Play Integrity.
שילוב עם AMAPI SDK באפליקציה
כדי לגשת לאותות המהימנות של המכשיר, האפליקציה צריכה להיות משולבת עם AMAPI SDK. במדריך לשילוב AMAPI SDK יש מידע נוסף על הספרייה הזו ועל אופן ההוספה שלה לאפליקציה.
הוספת ההרשאות הנדרשות
כדי לקבל חלק מהאותות שמוחזרים מ-Device Trust מ-Android Enterprise API, האפליקציה צריכה להצהיר על אותה הרשאה שנדרשת כדי לגשת למידע הזה מלכתחילה, ובמיוחד:
| אות | ההרשאה הנדרשת |
|---|---|
| מצב הרשת | ACCESS_NETWORK_STATE |
| מידת המורכבות של נעילת המסך | REQUEST_PASSWORD_COMPLEXITY |
אם ההרשאות האלה לא נכללות ב-AndroidManifest.xml של האפליקציה, ה-API של Device Trust מ-Android Enterprise יחזיר PERMISSION_ISSUE במטא-נתונים של האות שקשור להרשאות האלה:
internalDeviceSettings=DeviceSettings{
screenLockComplexity=COMPLEXITY_UNSPECIFIED,
internalScreenLockComplexityMetadata=Metadata{
dataIssues=[
DataIssue{
issueType=PERMISSION_ISSUE,
issueLevel=WARNING,
issueDetails=IssueDetailsCase{none}
}
]
},
פרטים נוספים מופיעים ברשימת אותות האמון הזמינים במכשיר.
שלבים לגישה לאותות האמון במכשיר
אפליקציות שרוצות לגשת לאותות האמון של המכשיר צריכות לוודא שהסביבה של הלקוח מעודכנת, ולעדכן אותה אם צריך.
כדי לגשת לאותות האמינות של המכשיר:

אימות סביבת הלקוח
בדוגמת הקוד הבאה אפשר לראות איך משתמשים ב-getEnvironment כדי לקרוא את המצב הנוכחי של אפליקציית ה-ADP. לאחר מכן, האפליקציה יכולה ליצור deviceClient כדי לגשת לאותות מהימנות המכשיר אם הסביבה מוכנה ומעודכנת (ראו גישה לאותות מהימנות המכשיר).
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 = DeviceClientFactory.create(context)) } 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.. DeviceClient deviceClient = DeviceClientFactory.create(context); 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()); }
אם אפליקציית ה-ADP מותקנת אבל לא מעודכנת, האפליקציה שלכם צריכה לקרוא ל-prepareEnvironment כדי לעדכן את אפליקציית ה-ADP בשקט בלי התערבות של המשתמש.
אם אפליקציית ה-ADP לא מותקנת, האפליקציה שלכם יכולה להתקשר אל prepareEnvironment
כדי להציג למשתמש בקשה להתקין את אפליקציית ה-ADP. מידע נוסף זמין במאמר בנושא הכנת סביבת הלקוח.
הכנת סביבת הלקוח
אם אפליקציית ADP כבר מותקנת, ה-API יעודכן בשקט ללא התערבות המשתמש.
אם אפליקציית ה-ADP לא מותקנת, ה-API יבקש מהמשתמש לאשר את ההתקנה של אפליקציית ה-ADP.
אפשר לרשום קריאה חוזרת כדי לעקוב אחרי הבחירה של המשתמש. פרטים נוספים זמינים במאמר מעקב אחרי אינטראקציות של משתמשים במהלך התקנת אפליקציית ADP.
מומלץ לבצע את הקריאה ל-prepareEnvironment מתהליך בחזית, במהלך תהליך ההצטרפות של חוויית המשתמש, כדי שהמשתמש לא יופתע מתיבת הדו-שיח המודאלית Install Android Device Policy.
אם אי אפשר לבצע קריאה מתהליך בחזית, כי מדובר בתהליך אינטרנטי ולרכיב Android אין ממשק משתמש, מותר לבצע קריאה מהרקע, בתנאי שהיא מתבצעת במהלך תהליך חוויית המשתמש של ההצטרפות.
אחרי שהסביבה מוגדרת בצורה נכונה, אפשר לגשת לאותות של אמון המכשיר. איך ניגשים לאותות מהימנות המכשיר
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 = DeviceClientFactory.create(context)) } 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.. DeviceClient deviceClient = DeviceClientFactory.create(context); 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()); }
גישה לאותות אמון במכשיר
כדי לגשת לאותות האמון במכשיר שמעניינים אתכם, אתם יכולים להשתמש במופע deviceClient שמופיע בשלב הקודם כדי לבקש את האובייקט Device.
Kotlin
try { 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) } } catch (e: java.lang.Exception) { Log.d(TAG, e.toString()) }
Java
try { 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, Log.d(TAG, t.toString()); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.d(TAG, e.toString()); }
טיפול בשגיאות
אם האפליקציה שולחת בקשת מהימנות למכשיר והשיחה נכשלת, האפליקציה מקבלת חריגה. יכולות להיות לכך כמה סיבות, כמו:
-
SecurityException– האפליקציה לא רשומה לגישה לאותות של מהימנות המכשיר. מידע נוסף על גישה לאותות אמינות של מכשירים - מחלקת משנה של
AmapiSdkException– ADP לא קיים או לא מוכן. יכול להיות שהבעיה זמנית, ולכן כדאי לנסות להתקשר שוב.
אסטרטגיות לניסיון חוזר
במקרה של חריגים שנובעים ממצב זמני, צריך להטמיע אסטרטגיה לביצוע ניסיון חוזר עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff). אחרי הכשל הראשון, מתחילים עם השהיה הראשונית של 5 שניות לפני שמנסים שוב.
כדאי להטמיע אסטרטגיה של ניסיונות חוזרים עם מספר מקסימלי של ניסיונות כתנאי יציאה, באמצעות הגדלה אקספוננציאלית של העיכוב בכל פעם (10 שניות, 20 שניות וכו').
מעקב אחר אינטראקציות של משתמשים במהלך התקנת אפליקציה ב-ADP
אם המכשיר צריך להתקין את אפליקציית ה-ADP במהלך prepareEnvironment, האפליקציה שלכם יכולה לעקוב אחרי האינטראקציה של המשתמש באמצעות הטמעה של NotificationReceiverService כדי לקבל התראות שדוחפות את 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"); } } }
בעיות מוכרות
אין בעיות ידועות כרגע.