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ınAndroidManifest.xmlbö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
Yönetmeyi planladığınız özel uygulamalarla bir
policyoluş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" } } ] } ```enterprises.enrollmentTokens.create işlevini
allowPersonalUsageparametresiPERSONAL_USAGE_DISALLOWEDolarak ayarlanmış şekilde çağırarak cihaz için bir kayıt jetonu oluşturun.Cihazı kayıt jetonuyla tümüyle yönetilen modda hazırlayın.
Genişletilebilirlik uygulamanızı Managed Play'den yükleyin.
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.
Özel uygulama politikadan kaldırılırsa,
playStoreModepolitikasına uygun olan, yan yüklenmiş uygulama kaldırılmaz.Daha ayrıntılı bilgi için
CUSTOMyükleme türünü inceleyin.
API
Sunucu-istemci API'si
Listelenen yeni alanlara ve numaralandırılmış değerlere bakın: