המסמך הזה הוא המדריך העיקרי לשימוש ב-AMAPI SDK לצורך קבלת אותות מהימנות של מכשירים.
ערכת ה-SDK של AMAPI מאפשרת לאפליקציה שלכם (שנקראת לפעמים אפליקציית 'עזר') לגשת לאותות מהימנות של המכשיר מאפליקציית ADP (מדיניות מכשירי Android). לאחר מכן, האפליקציה יכולה להשתמש באותות האלה כדי לחשב את מצב המהימנות של המכשיר וליישם לוגיקה עסקית לפי בחירתכם.
דרישות מוקדמות
- הגישה לאותות המהימנות של המכשיר מוגבלת כדי למנוע שימוש לא מורשה. מידע על אופן ההפעלה מופיע בדף גישה לאותות מהימנות של מכשירים.
- ב-Android Enterprise מומלץ לשלב את חבילת ממשקי Play Integrity API באפליקציית הלקוח, ולעיין בתוצאה לפני שקוראים את אותות המהימנות של המכשיר ומסתמכים עליהם. אסור לסמוך על מכשירים שנכשלים בבדיקות של Play Integrity API, וגם לא על אותות שמגיעים מהמכשיר ומשמשים לקביעת רמת האמון. פרטים נוספים זמינים במסמכים בנושא שלמות Play.
שילוב עם 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()); }
מעקב אחר אינטראקציות של משתמשים במהלך התקנה של אפליקציית 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"); } } }
בעיות מוכרות
אין בעיות ידועות כרגע.