يجب أن يكون هذا المستند دليلك الأساسي لاستخدام حزمة تطوير البرامج (SDK) الخاصة بواجهة برمجة التطبيقات لإدارة Android لأغراض تلقّي إشارات الثقة في الجهاز.
تتيح حزمة تطوير البرامج (SDK) لواجهة AMAPI لتطبيقك الوصول إلى إشارات ثقة الأجهزة من تطبيق Android Device Policy (ADP). ويمكن لتطبيقك بعد ذلك استخدام هذه الإشارات لاحتساب حالة ثقة الجهاز وتطبيق منطق النشاط التجاري حسب اختيارك.
المتطلبات الأساسية
- يجب أن يكون تطبيقك مصرَّحًا له بقراءة إشارات ثقة الأجهزة:
- التطبيقات المصاحبة على الأجهزة المُدارة من خلال AMAPI: يمكن للتطبيقات التي تم منحها دور
COMPANION_APPفي السياسة الوصول إلى إشارات الثقة في الجهاز بدون أن تكون مدرَجة في القائمة المسموح بها على الأجهزة المُدارة بالكامل أو من داخل ملف العمل (مع حصر الوصول بالإشارات الواردة من ملف العمل). لمعرفة التفاصيل، يُرجى الاطّلاع على إدارة أدوار التطبيقات وضبط السياسات وإشارات الوصول من تطبيق مصاحب. - جميع التطبيقات والبيئات الأخرى: يتطلب الوصول إلى إشارات ثقة الجهاز
من داخل ملف شخصي، على جهاز غير مُدار، على جهاز
مُدار بواسطة وحدة تحكّم بسياسة الجهاز (DPC) مخصّصة، أو بدون دور
COMPANION_APPأن يكون تطبيقك مدرجًا في القائمة المسموح بها. للحصول على معلومات حول كيفية تقديم الطلب، يُرجى الانتقال إلى صفحة الوصول إلى إشارات الثقة في الجهاز.
- التطبيقات المصاحبة على الأجهزة المُدارة من خلال AMAPI: يمكن للتطبيقات التي تم منحها دور
- ينصح Android Enterprise بدمج حزمة واجهات برمجة التطبيقات Play Integrity في تطبيق العميل والرجوع إلى النتيجة قبل قراءة إشارات الثقة في الجهاز والاعتماد عليها. لا يجب الوثوق بالأجهزة التي لا تجتاز عمليات التحقّق من واجهة برمجة التطبيقات Play Integrity API، ولا بأي إشارات مستمَدة من الجهاز المستخدَم لتحديد مستوى الثقة. يمكنك الرجوع إلى مستندات Play Integrity لمزيد من التفاصيل.
كيفية التكامل مع حزمة تطوير البرامج (SDK) لواجهة برمجة التطبيقات AMAPI في تطبيقك
للوصول إلى إشارات الثقة في الجهاز، يجب أن يتكامل تطبيقك مع حزمة تطوير البرامج (SDK) لواجهة برمجة التطبيقات Android Management API. يمكنك العثور على مزيد من المعلومات حول هذه المكتبة وكيفية إضافتها إلى تطبيقك في دليل دمج حزمة تطوير البرامج (SDK) لواجهة برمجة التطبيقات AMAPI.
إضافة الأذونات المطلوبة
تتطلّب بعض الإشارات التي يتم عرضها من واجهة برمجة التطبيقات Device Trust من Android Enterprise أن يصرّح التطبيق بالإذن نفسه المطلوب للوصول إلى هذه المعلومات في المقام الأول، لا سيما:
| إشارة | الإذن المطلوب |
|---|---|
| حالة الشبكة | ACCESS_NETWORK_STATE |
| مستوى تعقيد قفل الشاشة | REQUEST_PASSWORD_COMPLEXITY |
إذا لم يتم تضمين هذه الأذونات في AndroidManifest.xml الخاص بالتطبيق،
ستعرض واجهة برمجة التطبيقات 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) } 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) }
جافا
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()); }
إذا كان تطبيق ADP مثبّتًا ولكنّه ليس محدّثًا، يجب أن يستدعي تطبيقك
prepareEnvironment لتحديث تطبيق ADP تلقائيًا بدون تدخّل المستخدم.
إذا لم يكن تطبيق ADP مثبَّتًا، يمكن لتطبيقك استدعاء prepareEnvironment
لمطالبة المستخدم بتثبيت تطبيق ADP. يُرجى الاطّلاع على
إعداد بيئة العميل.
إعداد بيئة العميل
إذا كان تطبيق ADP مثبّتًا مسبقًا، ستحدّثه واجهة برمجة التطبيقات تلقائيًا بدون تدخّل المستخدم.
إذا لم يكن تطبيق ADP مثبّتًا، ستطلب واجهة برمجة التطبيقات من المستخدم قبول تثبيت تطبيق ADP.
يمكن تسجيل دالة استدعاء لمراقبة خيار المستخدم. لمزيد من التفاصيل، يُرجى الاطّلاع على مقالة تتبُّع تفاعل المستخدمين أثناء تثبيت تطبيق ADP.
ننصح بإجراء طلب prepareEnvironment من عملية تعمل في المقدّمة أثناء تدفق تجربة المستخدم للإعداد لتجنب مفاجأة المستخدم بمربع الحوار المشروط تثبيت سياسة جهاز Android.
إذا لم يكن من الممكن إجراء الطلب من عملية في المقدّمة، لأنّ هذا تدفق ويب
ولا يتضمّن مكوّن 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) } 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()) }
جافا
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()); }
الوصول إلى إشارات ثقة الأجهزة
اقتراح استخدام DeviceClient
يؤدي إنشاء مثيل deviceClient (أو مثيل EnvironmentClient) إلى إنشاء قناة اتصال مع تطبيق Android Device Policy (ADP) يتم إلغاء ربطها تلقائيًا عندما يكون التطبيق غير نشط. بما أنّ نُسخ العميل لا تعرض طريقة إيقاف صريحة، فإنّ إنشاء نُسخ متعددة وإلغاءها يترك قنوات معزولة ليتم جمعها في المهملات.
لتحسين الأداء وتجنُّب القنوات المعزولة، أنشئ مثيلاً واحدًا من
DeviceClient على مستوى التطبيق باستخدام
DeviceClientFactory.create(context.applicationContext) وخزِّنه مؤقتًا لإعادة استخدامه
في جميع أنحاء تطبيقك، على سبيل المثال، كعنصر فردي على مستوى التطبيق أو
باستخدام حقن التبعية.
للوصول إلى إشارات ثقة الجهاز التي تهمّك، استخدِم مثيل deviceClient المخزَّن مؤقتًا لطلب عنصر 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) }
جافا
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());
الوصول إلى الإشارات من تطبيق مصاحب
على الأجهزة المُدارة بالكامل أو من داخل ملف عمل مُدار باستخدام Android Management API (AMAPI)، يمكن لتطبيق تم منحه دور COMPANION_APP قراءة إشارات موثوقية الجهاز بدون تسجيله في القائمة المسموح بها.
عند استدعاء التطبيق من داخل ملف العمل (على جهاز تملكه الشركة أو جهاز شخصي)، يجب أن يعمل التطبيق داخل ملف العمل، ويقتصر الوصول على الإشارات الواردة من ملف العمل (على سبيل المثال، حالة الإدارة ومدى تعقيد قفل الشاشة والتطبيقات المثبَّتة تعكس ملف العمل فقط).
1. إسناد دور COMPANION_APP في السياسة
اضبط سياسة "إدارة الخدمات الجوّالة للمؤسسات" لمنح roleType: "COMPANION_APP" لتطبيقك في قائمة سياسات applications:
- لضبط ملف البيان الخاص بتطبيقك والاستماع اختياريًا إلى عمليات إسناد الأدوار، يُرجى الاطّلاع على إدارة أدوار التطبيق.
- للاطّلاع على بنية إعداد السياسة، يُرجى مراجعة إعداد السياسة.
2. قراءة الإشارات مباشرةً باستخدام DeviceClient
أنشِئ مثيل deviceClient واحدًا وخزِّنه مؤقتًا في نطاق التطبيق باستخدام
DeviceClientFactory.create (راجِع
اقتراح استخدام DeviceClient)، ثم
اطلب عنصر Device مباشرةً:
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" } }
جافا
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()); } }
معالجة الأخطاء
إذا قدّم تطبيقك طلبًا للتحقّق من موثوقية الجهاز وتعذّر إكمال الطلب، سيتلقّى تطبيقك استثناءً. يمكن أن تحدث هذه الاستثناءات لأسباب مختلفة، مثل:
-
SecurityException: تطبيقك غير مصرَّح له بتقديم الطلب (على سبيل المثال، لم يتم تسجيل تطبيقك في القائمة المسموح بها عند الحاجة، أو لم يتم منحه دورCOMPANION_APPعلى جهاز مُدار من خلال واجهة برمجة التطبيقات لإدارة Android، أو تم استدعاءprepareEnvironmentكتطبيق مصاحب غير مدرَج في القائمة المسموح بها). يُرجى الاطّلاع على الوصول إلى إشارات ثقة الأجهزة والوصول إلى الإشارات من تطبيق مصاحب. - فئة فرعية من
AmapiSdkException- ADP غير متوفّرة أو غير جاهزة. قد يكون السبب في ذلك حالة عابرة، لذا عليك إعادة محاولة إجراء المكالمة.
استراتيجيات إعادة المحاولة
بالنسبة إلى الاستثناءات الناتجة عن حالة عابرة، يمكنك تنفيذ استراتيجية إعادة المحاولة باستخدام خوارزمية الرقود الأسي الثنائي. بعد حدوث الخطأ الأول، ابدأ بمهلة أولية مدتها 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 } }
جافا
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"); } } }
المشاكل المعروفة
ما مِن مشاكل معروفة في الوقت الحالي.