به عنوان یک 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 ); }
۳. ارائه سیاستهای مدیریت برنامههای سفارشی به دستگاه
یک
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" } } ] } ```با فراخوانی enterprises.enrollmentTokens.create و تنظیم
allowPersonalUsageرویPERSONAL_USAGE_DISALLOWED، یک توکن ثبت نام برای دستگاه ایجاد کنید.دستگاه را در حالت کاملاً مدیریتشده با توکن ثبتنام (enrollment token) راهاندازی کنید .
برنامه توسعهپذیری خود را از Managed Play نصب کنید.
برنامه توسعهپذیری شما:
- میتوانید فایل APK برنامه سفارشی را دانلود کنید
- میتواند درخواستی برای نصب برنامه سفارشی صادر کند (به قطعه کدی که قبلاً نشان داده شده است مراجعه کنید)
- باید پاسخی دریافت کند
اگر برنامه سفارشی از خطمشی حذف شود، برنامه جانبی نصبشده در صورتی که با
playStoreModeمطابقت داشته باشد، حذف نصب نخواهد شد.برای جزئیات بیشتر، نوع نصب
CUSTOMinstall type) را بررسی کنید.
رابط برنامهنویسی کاربردی
رابط برنامهنویسی کاربردی (API) سرور-کلاینت
به فیلدها و enumهای جدید ذکر شده مراجعه کنید: