AMAPI ile özel uygulamaları yönetme

Android Management API tabanlı bir EMM olarak, cihazlardaki özel uygulamaları uzaktan yönetebilirsiniz. Bu, söz konusu uygulamaların yüklenmesi ve kaldırılması işlemlerini kapsar. Bu işlev, AMAPI SDK kullanılarak yerel olarak bir uzantı uygulaması geliştirilerek sağlanır.

Ön koşullar

  • Uzantı uygulamanız AMAPI SDK ile entegre edilmiş olmalıdır.
  • Cihaz tümüyle yönetiliyor olmalıdır.
  • AMAPI SDK v1.6.0-rc01 veya sonraki bir sürümünün kullanılması gereklidir.

1. Uygulamanızı özelliği kullanmaya hazırlama

1.1. Uzantı uygulamanızda AMAPI SDK'sı ile entegrasyon yapma

Özel uygulama yönetimi süreci, AMAPI SDK'sını uzantı uygulamanıza entegre etmenizi gerektirir. Bu kitaplık ve uygulamanıza nasıl ekleyeceğiniz hakkında daha fazla bilgiyi AMAPI SDK entegrasyon kılavuzunda bulabilirsiniz.

1.2. Uygulamanızın manifestini FileProvider'ı destekleyecek şekilde güncelleyin

  • AndroidManifest.xml öğesini, <queries> Android Cihaz Politikası (ADP) uygulaması için AMAPI SDK entegrasyon kılavuzunda gösterildiği gibi ekleyin .
  • Aşağıdaki <provider> snippet'ini uygulamanızın AndroidManifest.xml bölümündeki <application> etiketine uygulayın. Bu snippet, özel uygulama APK'sı paylaşılırken dosyaları depolamak ve AMAPI kullanılarak özel uygulamaların yüklenmesini sağlamak için kullanılır.

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.customapp">
  <queries>
    <package android:name="com.google.android.apps.work.clouddpc" />
  </queries>

  <application>

    <!--This is used to store files when sharing the custom app apk.-->
    <provider
        android:name="com.google.android.managementapi.customapp.provider.CustomAppProvider"
        android:authorities="${applicationId}.AmapiCustomAppProvider"
        android:exported="false"
        android:grantUriPermissions="true">
      <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_provider_paths" />
    </provider>
  </application>
</manifest>
  • Uygulamanızın res/xml/ dizininde, özel APK'lerin depolama yolunu içeren yeni bir XML dosyası oluşturun.

file_provider_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <cache-path
      name="android_managementapi_custom_apks"
      path="com.google.android.managementapi/customapp/apks/" />
</paths>

2. AMAPI SDK'sının özel uygulama özelliğiyle entegrasyon

2.1. Özel APK dosyasını yüklemeye hazırlama

Uygulamanın APK dosyası, dağıtımdan önce yüklemeye hazırlanmalıdır. Aşağıdaki kod snippet'inde bu işlem gösterilmektedir:

Kotlin

import android.net.Uri
import androidx.core.net.Uri
import java.io.File
...
import com.google.android.managementapi.commands.LocalCommandClient
import com.google.android.managementapi.commands.LocalCommandClient.InstallCustomAppCommandHelper
import com.google.android.managementapi.commands.LocalCommandClientFactory

...

fun prepareApkFile(): Uri? {
    // Get the storage location of custom APK files from AM API
    val client: LocalCommandClient = LocalCommandClientFactory.create(context)
    val installCustomAppCommandHelper = client.installCustomAppCommandHelper

    val customApksStorageDir: File = installCustomAppCommandHelper.customApksStorageDirectory ?: return null

    // Once you get hold of the custom APKs storage directory, you must store your custom APK
    // in that location before issuing the install command.
    val customApkFile: File = fetchMyAppToDir(customApksStorageDir) ?: return null
    val customApkFileUri: Uri = customApkFile.toUri()

    return customApkFileUri
}

Java

import android.net.Uri;
import androidx.core.net.Uri;
import java.io.File;
...
import com.google.android.managementapi.commands.LocalCommandClient;
import com.google.android.managementapi.commands.LocalCommandClient.InstallCustomAppCommandHelper;
import com.google.android.managementapi.commands.LocalCommandClientFactory;

...

Uri prepareApkFile() {
  // Get the storage location of custom APK files from AM API
  LocalCommandClient client = LocalCommandClientFactory.create();
  InstallCustomAppCommandHelper installCustomAppCommandHelper = client.getInstallCustomAppCommandHelper();
  File customApksStorageDir = installCustomAppCommandHelper.getCustomApksStorageDirectory();

  // Once you get hold of the custom APKs storage directory, you must store your custom APK
  // in that location before issuing the install command.
  File customApkFile = fetchMyAppToDir(customApksStorageDir);
  Uri customApkFileUri = Uri.fromFile(customApkFile);
  ...
}

2.2. Özel uygulama yükleme isteği gönderme

Aşağıdaki snippet'te, özel bir uygulamayı yükleme isteğinin nasıl gönderileceği gösterilmektedir:

Kotlin

import android.content.Context
import android.net.Uri
import android.util.Log
import com.google.android.managementapi.commands.LocalCommandClientFactory
import com.google.android.managementapi.commands.model.Command
import com.google.android.managementapi.commands.model.IssueCommandRequest
import com.google.android.managementapi.commands.model.IssueCommandRequest.InstallCustomApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.guava.await
import kotlinx.coroutines.withContext
import java.lang.Exception

private const val TAG = "MyClass"

...

    // Requires a file URI of the APK file.
    fun issueInstallCustomAppCommand(packageName: String, fileUri: Uri) {
        coroutineScope.launch {
            try {
                withContext(coroutineScope.coroutineContext) {
                    val result: Command = LocalCommandClientFactory.create(context)
                        .issueCommand(createInstallCustomAppRequest(packageName, fileUri)).await()
                    // Process the returned command result here.
                    Log.i(TAG, "Successfully issued command: $result")
                }
            } catch (t: Exception) {
                Log.e(TAG, "Failed to issue command", t)
                // Handle the exception (e.g., show an error message)
            } finally {
                // Make sure to clean up the apk file after the command is executed.
                cleanUpApkFile(fileUri)
            }
        }
    }

    private fun createInstallCustomAppRequest(packageName: String, fileUri: Uri): IssueCommandRequest {
        return IssueCommandRequest.builder()
            .setInstallCustomApp(
                InstallCustomApp.builder()
                    .setPackageName(packageName)
                    .setPackageUri(fileUri.toString())
                    .build()
            )
            .build()
    }
}

Java

import android.util.Log;
...
import com.google.android.managementapi.commands.LocalCommandClientFactory;
import com.google.android.managementapi.commands.model.Command;
import com.google.android.managementapi.commands.model.GetCommandRequest;
import com.google.android.managementapi.commands.model.IssueCommandRequest;
import com.google.android.managementapi.commands.model.IssueCommandRequest.ClearAppsData;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;

...

  // Requires a file URI of the APK file.
  void issueInstallCustomAppCommand(String packageName, Uri fileUri) {
    Futures.addCallback(
        LocalCommandClientFactory.create(getContext())
            .issueCommand(createInstallCustomAppRequest(packageName, fileUri)),
        new FutureCallback() {
          @Override
          public void onSuccess(Command result) {
            // Process the returned command result here.
            Log.i(TAG, "Successfully issued command");
          }

          @Override
          public void onFailure(Throwable t) {
            Log.e(TAG, "Failed to issue command", t);
          }
        },
        MoreExecutors.directExecutor());
  }

  IssueCommandRequest createInstallCustomAppRequest(String packageName, Uri fileUri) {
    return IssueCommandRequest.builder()
        .setInstallCustomApp(
            InstallCustomApp.builder()
                .setPackageName(packageName)
                .setPackageUri(fileUri.toString())
                .build()
        )
        .build();
  }

2.3. Yüklü uygulamaları almak için istekte bulunma

Kotlin

import android.content.Context
import com.google.android.managementapi.device.DeviceClientFactory
import com.google.android.managementapi.device.model.GetDeviceRequest
import kotlinx.coroutines.guava.await

  suspend fun getInstalledApps(context: Context) =
    DeviceClientFactory.create(context)
      .getDevice(GetDeviceRequest.getDefaultInstance())
      .await()
      .getApplicationReports()

Java

import android.content.Context;
import com.google.android.managementapi.device.DeviceClientFactory;
import com.google.android.managementapi.device.model.GetDeviceRequest;
import com.google.android.managementapi.device.model.Device;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.List;
import java.util.concurrent.Executor;

public ListenableFuture<List> getInstalledApps() {
        ListenableFuture deviceFuture =
            DeviceClientFactory.create(context)
                .getDevice(GetDeviceRequest.getDefaultInstance());

        return Futures.transform(
            deviceFuture,
            Device::getApplicationReports,
            executor // Use the provided executor
        );
    }

3. Cihazı özel uygulama yönetimi politikalarıyla sağlama

  1. Yönetmeyi planladığınız özel uygulamalarla bir policy oluşturun.

      {
        "statusReportingSettings": {
          "applicationReportsEnabled": true
        },
        "applications": [
        {
          "signingKeyCerts": [
            {
            "signingKeyCertFingerprintSha256": <sha256 signing key certificate hash value>
            }
          ],
          "packageName": "<emm_extensibility_app>",
          "installType": "AVAILABLE",
          "defaultPermissionPolicy": "GRANT",
          "extensionConfig": {
              "notificationReceiver": "com.example.customapp.NotificationReceiverService"
          }
        },
        {
          "signingKeyCerts": [
            {
            "signingKeyCertFingerprintSha256": <sha256 signing key certificate hash value>
            },
          ],
          "packageName": "<custom_app>",
          "installType": "CUSTOM",
          "defaultPermissionPolicy": "GRANT",
          "customAppConfig": {
              "userUninstallSettings": "DISALLOW_UNINSTALL_BY_USER"
          }
        }
        ]
      }
      ```
    
  2. enterprises.enrollmentTokens.create işlevini allowPersonalUsage parametresi PERSONAL_USAGE_DISALLOWED olarak ayarlanmış şekilde çağırarak cihaz için bir kayıt jetonu oluşturun.

  3. Cihazı kayıt jetonuyla tümüyle yönetilen modda hazırlayın.

  4. Genişletilebilirlik uygulamanızı Managed Play'den yükleyin.

  5. Genişletilebilirlik uygulamanız:

    • Özel uygulamanın APK dosyasını indirebilir.
    • Özel uygulamanın yüklenmesi için istekte bulunabilir (daha önce gösterilen kod snippet'ine bakın).
    • yanıt almalıdır.
  6. Özel uygulama politikadan kaldırılırsa, playStoreMode politikasına uygun olan, yan yüklenmiş uygulama kaldırılmaz.

  7. Daha ayrıntılı bilgi için CUSTOM yükleme türünü inceleyin.

API

Sunucu-istemci API'si

Listelenen yeni alanlara ve numaralandırılmış değerlere bakın: