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

.

המסמך הזה הוא המדריך העיקרי לשימוש ב-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();

    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");
        }
    }
}

בעיות מוכרות

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