Device Trust from Android Enterprise – מדריך לשילוב

המסמך הזה אמור לשמש כמדריך הראשי לשימוש ב-AMAPI SDK לצורך קבלת אותות אמון מהמכשיר.

ערכת ה-SDK של AMAPI מאפשרת לאפליקציה שלכם (שנקראת לפעמים 'אפליקציית שותף') לגשת לאותות האמון של המכשיר מאפליקציית ADP‏ (Android Device Policy). לאחר מכן, האפליקציה יכולה להשתמש באותם אותות כדי לחשב את מצב האמון של המכשיר ולהפעיל את הלוגיקה העסקית שנבחרה.

דרישות מוקדמות

  • הגישה לאותות האמינות של המכשיר מוגבלת כדי למנוע שימוש לא מורשה. מידע על תהליך הגשת הבקשה זמין בדף גישה לסמלי האמון של המכשיר.
  • ב-Android Enterprise מומלץ לשלב את חבילת ממשקי ה-API של Play Integrity באפליקציית הלקוח, ולהתייחס לתוצאה לפני קריאת אותות האמון של המכשיר והסתמכות עליהם. אין לסמוך על מכשירים שלא עומדים בבדיקות של Play Integrity API, או על אותות שמקורם במכשיר שמשמש לקביעת מצב האמון. פרטים נוספים זמינים במסמכי התיעוד של Play Integrity.

שילוב עם AMAPI SDK באפליקציה

כדי לגשת לאותות האמון של המכשיר, האפליקציה צריכה לשלב את AMAPI SDK. מידע נוסף על הספרייה הזו ועל האופן שבו מוסיפים אותה לאפליקציה זמין במדריך לשילוב AMAPI SDK.

הוספת ההרשאות הנדרשות

כדי לקבל חלק מהאותות שמוחזרים מ-Device Trust מ-Android Enterprise API, האפליקציה צריכה להצהיר על אותה הרשאה שנדרשת כדי לגשת למידע הזה מלכתחילה, במיוחד:

אות ההרשאה הנדרשת
מצב הרשת ACCESS_NETWORK_STATE
מידת המורכבות של נעילת המסך REQUEST_PASSWORD_COMPLEXITY

אם ההרשאות האלה לא נכללות ב-AndroidManifest.xml של האפליקציה, ה-Device Trust מ-Android Enterprise API יחזיר את הערך 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();

    ImmutableList roles = 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.

צריך להתקין את &#39;‏Device Policy למכשירי Android&#39;

אפשר להירשם לשיחה חוזרת כדי לעקוב אחרי הבחירה של המשתמש. פרטים נוספים זמינים במאמר מעקב אחר אינטראקציות של משתמשים במהלך התקנת אפליקציית 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
    );

    ImmutableList roles = 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 {
    ListenableFuture deviceFuture =
            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");
        }
    }
}

בעיות מוכרות

אין כרגע בעיות ידועות.