برنامه های سفارشی را با AMAPI مدیریت کنید

به عنوان یک EMM مبتنی بر API مدیریت اندروید، می‌توانید از راه دور برنامه‌های سفارشی را روی دستگاه‌ها مدیریت کنید. این شامل نصب و حذف این برنامه‌ها می‌شود. این قابلیت با توسعه یک برنامه افزودنی به صورت محلی با استفاده از AMAPI SDK حاصل می‌شود.

پیش‌نیازها

  • برنامه افزونه شما با AMAPI SDK یکپارچه شده است.
  • دستگاه کاملاً مدیریت شده است.
  • AMAPI SDK نسخه ۱.۶.۰-rc۰۱ یا بالاتر مورد نیاز است.

۱. برنامه خود را برای استفاده از این ویژگی آماده کنید

۱.۱. ادغام با AMAPI SDK در برنامه افزونه شما

فرآیند مدیریت برنامه سفارشی مستلزم آن است که AMAPI SDK را در برنامه افزونه خود ادغام کنید. می‌توانید اطلاعات بیشتر در مورد این کتابخانه و نحوه افزودن آن به برنامه خود را در راهنمای ادغام AMAPI SDK بیابید.

۱.۲. مانیفست برنامه خود را برای پشتیبانی FileProvider به‌روزرسانی کنید

  • همانطور که در راهنمای ادغام AMAPI SDK نشان داده شده است، عنصر <queries> را برای برنامه Android Device Policy (ADP) به AndroidManifest.xml خود اضافه کنید.
  • قطعه کد <provider> زیر را در فایل AndroidManifest.xml برنامه خود، درون تگ <application> پیاده‌سازی کنید. این قطعه کد برای ذخیره فایل‌ها هنگام اشتراک‌گذاری APK برنامه سفارشی استفاده می‌شود و نصب برنامه‌های سفارشی را با استفاده از AMAPI امکان‌پذیر می‌سازد.

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>
  • یک فایل XML جدید در دایرکتوری res/xml/ برنامه خود ایجاد کنید که حاوی مسیر ذخیره‌سازی فایل‌های apk سفارشی باشد.

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>

۲. ادغام با ویژگی برنامه سفارشی AMAPI SDK

۲.۱. فایل APK سفارشی را برای نصب آماده کنید

قبل از استقرار، فایل APK برنامه باید برای نصب آماده شود. قطعه کد زیر این فرآیند را نشان می‌دهد:

کاتلین

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
}

جاوا

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

۲.۲. درخواست نصب یک برنامه سفارشی را صادر کنید

قطعه کد زیر نحوه ارسال درخواست نصب یک برنامه سفارشی را نشان می‌دهد:

کاتلین

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

جاوا

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

۲.۳. درخواستی برای نصب برنامه‌ها ارسال کنید

کاتلین

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

جاوا

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

۳. ارائه سیاست‌های مدیریت برنامه‌های سفارشی به دستگاه

  1. یک policy برای برنامه‌های سفارشی که قصد مدیریت آنها را دارید، تنظیم کنید.

      {
        "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 و تنظیم allowPersonalUsage روی PERSONAL_USAGE_DISALLOWED ، یک توکن ثبت نام برای دستگاه ایجاد کنید.

  3. دستگاه را در حالت کاملاً مدیریت‌شده با توکن ثبت‌نام (enrollment token) راه‌اندازی کنید .

  4. برنامه توسعه‌پذیری خود را از Managed Play نصب کنید.

  5. برنامه توسعه‌پذیری شما:

    • می‌توانید فایل APK برنامه سفارشی را دانلود کنید
    • می‌تواند درخواستی برای نصب برنامه سفارشی صادر کند (به قطعه کدی که قبلاً نشان داده شده است مراجعه کنید)
    • باید پاسخی دریافت کند
  6. اگر برنامه سفارشی از خط‌مشی حذف شود، برنامه جانبی نصب‌شده در صورتی که با playStoreMode مطابقت داشته باشد، حذف نصب نخواهد شد.

  7. برای جزئیات بیشتر، نوع نصب CUSTOM install type) را بررسی کنید.

رابط برنامه‌نویسی کاربردی

رابط برنامه‌نویسی کاربردی (API) سرور-کلاینت

به فیلدها و enumهای جدید ذکر شده مراجعه کنید: