Android Enterprise'dan Device Trust - Entegrasyon rehberi

Cihaz güven sinyallerini almak amacıyla AMAPI SDK'nın kullanımı için bu belgeyi birincil kılavuzunuz olarak kullanmanız gerekir.

AMAPI SDK'sı, uygulamanızın Android Cihaz Politikası (ADP) uygulamasındaki Device Trust sinyallerine erişmesine olanak tanır. Uygulamanız daha sonra bu sinyalleri kullanarak cihazın güven durumunu hesaplayabilir ve seçilen iş mantığını uygulayabilir.

Ön koşullar

  • Uygulamanızın, Device Trust sinyallerini okuma yetkisi olmalıdır:
    • AMAPI tarafından yönetilen cihazlardaki tamamlayıcı uygulamalar: Politikada COMPANION_APP rolü atanmış uygulamalar, tümüyle yönetilen cihazlarda veya bir iş profilinin içinden (erişim, iş profilinden gelen sinyallerle sınırlı olacak şekilde) izin verilenler listesinde yer almadan cihaz güven sinyallerine erişebilir. Ayrıntılar için Uygulama rollerini yönetme, Politika yapılandırması ve Tamamlayıcı uygulamadan gelen sinyallere erişme başlıklı makaleleri inceleyin.
    • Diğer tüm uygulamalar ve ortamlar: Kişisel profilden, yönetilmeyen bir cihazdan, özel DPC tarafından yönetilen bir cihazdan veya COMPANION_APP rolü olmadan cihaz güven sinyallerine erişmek için uygulamanızın izin verilenler listesinde olması gerekir. Nasıl başvuracağınızla ilgili bilgi edinmek için cihaz güven sinyalleri erişimi sayfasına gidin.
  • Android Enterprise, Play Integrity API paketini istemci uygulamanıza entegre etmenizi ve cihaz güven sinyallerini okuyup bunlara güvenmeden önce sonucu incelemenizi önerir. Play Integrity API kontrollerinde başarısız olan cihazlara ve güvenilirlik durumu belirlemek için kullanılan cihazdan alınan sinyallere güvenilmemelidir. Daha fazla bilgi için Play Integrity belgelerini inceleyebilirsiniz.

Uygulamanızda AMAPI SDK'sı ile entegrasyon

Cihaz güven sinyallerine erişmek için uygulamanızın AMAPI SDK ile entegre edilmesi gerekir. Bu kitaplık ve uygulamanıza nasıl ekleyeceğiniz hakkında daha fazla bilgiyi AMAPI SDK entegrasyon kılavuzunda bulabilirsiniz.

Gerekli izinleri ekleme

Android Enterprise API'nin Device Trust özelliğinden döndürülen bazı sinyaller, uygulamanın bu bilgilere erişmek için gereken izni beyan etmesini gerektirir. Bu izinler şunlardır:

Sinyal Gerekli izin
Ağ Durumu ACCESS_NETWORK_STATE
Ekran kilidi karmaşıklığı REQUEST_PASSWORD_COMPLEXITY

Bu izinler uygulamanın AndroidManifest.xml dosyasında yer almıyorsa Android Enterprise'dan Device Trust API, ilgili sinyalin meta verilerinde PERMISSION_ISSUE değerini döndürür:

internalDeviceSettings=DeviceSettings{
  screenLockComplexity=COMPLEXITY_UNSPECIFIED,
  internalScreenLockComplexityMetadata=Metadata{
    dataIssues=[
      DataIssue{
        issueType=PERMISSION_ISSUE,
        issueLevel=WARNING,
        issueDetails=IssueDetailsCase{none}
      }
    ]
  },

Daha fazla bilgi için kullanılabilir cihaz güven sinyalleri listesine bakın.

Device Trust sinyallerine erişme adımları

İzin verilenler listesindeki uygulamalar (kişisel profilde veya yönetilmeyen bir cihazda çalışan uygulamalar dahil), sinyal istemeden önce istemci ortamının güncel olduğunu doğrulamalı ve gerekirse hazırlamalıdır.

İzin verilenler listesindeki uygulamaların Device Trust sinyallerine erişmesi için gereken adımlar şunlardır:

Device Trust sinyallerine erişme adımları

Müşteri ortamını doğrulama

Aşağıdaki kod örneğinde, ADP uygulamasının mevcut durumunu okumak için getEnvironment'nin nasıl kullanılacağı gösterilmektedir. Ortam hazır ve güncelse uygulamanız, cihaz güven sinyallerine erişmek için önbelleğe alınmış bir deviceClient örneği kullanabilir (Cihaz güven sinyallerine erişme bölümüne bakın).

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

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..
                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 uygulaması yüklüyse ancak güncel değilse uygulamanız, kullanıcı müdahalesi olmadan ADP uygulamasını sessizce güncellemek için prepareEnvironment yöntemini çağırmalıdır.

ADP uygulaması yüklü değilse uygulamanız prepareEnvironment yöntemini çağırarak kullanıcıdan ADP uygulamasını yüklemesini isteyebilir. İstemci ortamını hazırlama başlıklı makaleyi inceleyin.

İstemci ortamını hazırlama

  • ADP uygulaması zaten yüklüyse API, kullanıcı müdahalesi olmadan uygulamayı sessizce günceller.

  • ADP uygulaması yüklü değilse API, kullanıcıdan ADP uygulamasının yüklenmesini kabul etmesini ister.

Android Device Policy'yi yükle

Kullanıcı tercihini izlemek için bir geri çağırma işlevi kaydedebilirsiniz. Daha fazla bilgi için ADP uygulaması yükleme sırasında kullanıcı etkileşimini izleme başlıklı makaleyi inceleyin.

Kullanıcıyı Android Device Policy'yi yükle kalıcı iletişim kutusuyla şaşırtmamak için prepareEnvironment çağrısının, ilk katılım kullanıcı deneyimi akışı sırasında bir ön plan işleminden yapılmasını öneririz. Bu bir web akışı olduğundan ve Android bileşeninin kullanıcı arayüzü olmadığından ön plan işleminden çağrı yapmak mümkün değilse oryantasyon kullanıcı deneyimi akışı sırasında gerçekleşmesi şartıyla arka plandan çağrı yapılmasına izin verilir.

Ortam doğru şekilde ayarlandıktan sonra cihaz güven sinyallerine erişmek mümkündür. Device Trust sinyallerine erişme başlıklı makaleyi inceleyin.

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())
}

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..
                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());
}

Cihaz güven sinyallerine erişme

DeviceClient kullanım önerisi

deviceClient örneği (veya EnvironmentClient örneği) oluşturmak, Android Cihaz Politikası (ADP) uygulamasıyla bir iletişim kanalı oluşturur. Bu kanal, boştayken otomatik olarak ayrılır. İstemci örnekleri açık bir kapatma yöntemi sunmadığından, birden fazla örnek oluşturup silmek, artık kanalların çöp olarak toplanmasına neden olur.

Performansı optimize etmek ve sahipsiz kanalları önlemek için DeviceClientFactory.create(context.applicationContext) kullanarak uygulama kapsamında tek bir DeviceClient örneği oluşturun ve uygulamanız genelinde yeniden kullanmak üzere önbelleğe alın (ör. uygulama kapsamlı bir singleton olarak veya bağımlılık ekleme kullanarak).

İlgilendiğiniz cihaz güven sinyallerine erişmek için önbelleğe alınmış deviceClient örneğinizi kullanarak Device nesnesini isteyin:

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

Java

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, t.toString());
    }
}, MoreExecutors.directExecutor());

Tamamlayıcı uygulamadan gelen sinyallere erişme

Tümüyle yönetilen cihazlarda veya Android Management API (AMAPI) ile yönetilen bir iş profilinde, COMPANION_APP rolü atanmış bir uygulama, izin verilenler listesine kaydedilmeden cihaz güven sinyallerini okuyabilir.

Bir iş profilinden (şirkete ait veya kişisel bir cihazda) çağrıldığında uygulamanız iş profilinde çalışmalıdır ve erişim, iş profilinden gelen sinyallerle sınırlıdır (örneğin, yönetim durumu, ekran kilidi karmaşıklığı ve yüklü uygulamalar yalnızca iş profilini yansıtır).

1. Politikada COMPANION_APP rolünü atama

applications politika listesinde uygulamanıza roleType: "COMPANION_APP" izni vermek için EMM politikanızı yapılandırın:

  • Uygulamanızın manifest dosyasını yapılandırmak ve isteğe bağlı olarak rol atamalarını dinlemek için Uygulama rollerini yönetme başlıklı makaleyi inceleyin.
  • Politika yapılandırma söz dizimi için Politika Yapılandırması başlıklı makaleyi inceleyin.

2. Sinyalleri doğrudan DeviceClient ile okuma

DeviceClientFactory.create kullanarak uygulama kapsamında tek bir deviceClient örneği oluşturup önbelleğe alın (DeviceClient kullanım önerisi bölümüne bakın), ardından Device nesnesini doğrudan isteyin:

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

Java

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() {
        ListenableFuture deviceFuture =
                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());
    }
}

Hataları işleme

Uygulamanız cihaz güveni isteğinde bulunur ve çağrı başarısız olursa uygulamanız bir istisna alır. Bu istisnalar, aşağıdakiler gibi çeşitli nedenlerden kaynaklanabilir:

Yeniden deneme stratejileri

Geçici bir durumdan kaynaklanan istisnalar için eksponansiyel geri yükleme ile yeniden deneme stratejisi uygulayın. İlk başarısız denemeden sonra, yeniden denemeden önce 5 saniyelik bir başlangıç gecikmesiyle başlayın.

Çıkış koşulu olarak maksimum deneme sayısına sahip bir yeniden deneme stratejisi uygulayın ve her seferinde üstel olarak artan bir gecikme (10 sn, 20 sn vb.) kullanın.

ADP uygulaması yüklenirken kullanıcı etkileşimini izleme

Cihazın prepareEnvironment sırasında ADP uygulamasını yüklemesi gerekiyorsa uygulamanız, getPrepareEnvironmentListener'i geçersiz kılan bildirimleri almak için NotificationReceiverService uygulayarak kullanıcı etkileşimini izleyebilir:

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

Bilinen sorunlar

Şu anda bilinen bir sorun yoktur.