ในฐานะ EMM ที่ใช้ Android Management API คุณจะจัดการแอปพลิเคชันที่กำหนดเองในอุปกรณ์จากระยะไกลได้ ซึ่งรวมถึงการติดตั้งและถอนการติดตั้งแอปเหล่านี้ ฟังก์ชันนี้ทำได้โดยการพัฒนาแอปส่วนขยายในเครื่อง โดยใช้ AMAPI SDK
ข้อกำหนดเบื้องต้น
- แอปส่วนขยายของคุณผสานรวมกับ AMAPI SDK
- อุปกรณ์มีการจัดการครบวงจร
- ต้องใช้ AMAPI SDK เวอร์ชัน 1.6.0-rc01 ขึ้นไป
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. จัดสรรอุปกรณ์ด้วยนโยบายการจัดการแอปที่กำหนดเอง
ตั้งค่า
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" } } ] } ```
สร้างโทเค็นการลงทะเบียนสำหรับอุปกรณ์โดยเรียกใช้ enterprises.enrollmentTokens.create โดยตั้งค่า
allowPersonalUsage
เป็นPERSONAL_USAGE_DISALLOWED
จัดสรรอุปกรณ์ในโหมดมีการจัดการครบวงจรด้วยโทเค็นการลงทะเบียน
ติดตั้งแอปความสามารถในการขยายจาก Managed Play
แอปความสามารถในการขยายของคุณ
- ดาวน์โหลดไฟล์ APK ของแอปที่กำหนดเองได้
- สามารถส่งคำขอเพื่อติดตั้งแอปที่กำหนดเอง (ดูข้อมูลโค้ด ที่แสดงก่อนหน้านี้)
- ควรได้รับคำตอบ
API
API ไคลเอ็นต์เซิร์ฟเวอร์
โปรดดูช่องและ Enum ใหม่ที่แสดงไว้