จัดการแอปที่กําหนดเองด้วย AMAPI

ในฐานะ EMM ที่ใช้ Android Management API คุณจะจัดการแอปพลิเคชันที่กำหนดเองในอุปกรณ์จากระยะไกลได้ ซึ่งรวมถึงการติดตั้งและถอนการติดตั้งแอปเหล่านี้ ฟังก์ชันนี้ทำได้โดยการพัฒนาแอปส่วนขยายในเครื่อง โดยใช้ AMAPI SDK

ข้อกำหนดเบื้องต้น

1. เตรียมแอปให้พร้อมสำหรับการใช้ฟีเจอร์

1.1 ผสานรวมกับ AMAPI SDK ในแอปส่วนขยาย

กระบวนการจัดการแอปที่กำหนดเองกำหนดให้คุณต้องผสานรวม AMAPI SDK ใน แอปส่วนขยายของคุณ ดูข้อมูลเพิ่มเติมเกี่ยวกับไลบรารีนี้และวิธี เพิ่มลงในแอปได้ในคู่มือการผสานรวม AMAPI SDK

1.2 อัปเดตไฟล์ Manifest ของแอปเพื่อรองรับ FileProvider

  • เพิ่มองค์ประกอบ AndroidManifest.xml<queries> สำหรับแอปพลิเคชันนโยบายอุปกรณ์ Android (ADP) ลงใน คู่มือการผสานรวม AMAPI SDK
  • ใช้ข้อมูลโค้ด <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>

2. ผสานรวมกับฟีเจอร์แอปที่กำหนดเองของ AMAPI SDK

2.1 เตรียมไฟล์ APK ที่กำหนดเองสำหรับการติดตั้ง

ก่อนที่จะติดตั้งใช้งาน คุณต้องเตรียมไฟล์ APK ของแอปพลิเคชันสำหรับการติดตั้ง ข้อมูลโค้ดต่อไปนี้แสดงกระบวนการ

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 ส่งคำขอติดตั้งแอปที่กำหนดเอง

ข้อมูลโค้ดต่อไปนี้แสดงวิธีส่งคำขอติดตั้งแอปที่กำหนดเอง

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 ส่งคำขอเพื่อรับแอปที่ติดตั้ง

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. จัดสรรอุปกรณ์ด้วยนโยบายการจัดการแอปที่กำหนดเอง

  1. ตั้งค่า policy ด้วยแอปที่กำหนดเองที่คุณต้องการจัดการ

      {
        "statusReportingSettings": {
          "applicationReportsEnabled": true
        },
        "applications": [
        {
          "signingKeyCerts": [
            {
            "signingKeyCertFingerprintSha256": <sha256 signing key certificate hash value>
            }
          ],
          "packageName": "<emm_extensibility_app>",
          "installType": "AVAILABLE",
          "lockTaskAllowed": true,
          "defaultPermissionPolicy": "GRANT",
          "extensionConfig": {
              "notificationReceiver": "com.example.customapp.NotificationReceiverService"
          }
        },
        {
          "signingKeyCerts": [
            {
            "signingKeyCertFingerprintSha256": <sha256 signing key certificate hash value>
            },
          ],
          "packageName": "<custom_app>",
          "installType": "CUSTOM",
          "lockTaskAllowed": true,
          "defaultPermissionPolicy": "GRANT",
          "customAppConfig": {
        "userUninstallSettings": "DISALLOW_UNINSTALL_BY_USER"
          }
        }
        ]
      }
      ```
    
  2. สร้างโทเค็นการลงทะเบียนสำหรับอุปกรณ์โดยเรียกใช้ enterprises.enrollmentTokens.create โดยตั้งค่า allowPersonalUsage เป็น PERSONAL_USAGE_DISALLOWED

  3. จัดสรรอุปกรณ์ในโหมดมีการจัดการครบวงจรด้วยโทเค็นการลงทะเบียน

  4. ติดตั้งแอปความสามารถในการขยายจาก Managed Play

  5. แอปความสามารถในการขยายของคุณ

    • ดาวน์โหลดไฟล์ APK ของแอปที่กำหนดเองได้
    • สามารถส่งคำขอเพื่อติดตั้งแอปที่กำหนดเอง (ดูข้อมูลโค้ด ที่แสดงก่อนหน้านี้)
    • ควรได้รับคำตอบ

API

API ไคลเอ็นต์เซิร์ฟเวอร์

โปรดดูช่องและ Enum ใหม่ที่แสดงไว้