Als EMM-Anbieter, der auf der Android Management API basiert, können Sie benutzerdefinierte Anwendungen auf Geräten per Fernzugriff verwalten. Das gilt sowohl für die Installation als auch für die Deinstallation dieser Apps. Diese Funktion wird durch die lokale Entwicklung einer Erweiterungs-App mit dem AMAPI SDK erreicht.
Vorbereitung
- Ihre Erweiterungs-App ist in das AMAPI SDK integriert.
- Das Gerät ist vollständig verwaltet.
- AMAPI SDK v1.6.0-rc01 oder höher ist erforderlich.
1. App für die Verwendung der Funktion vorbereiten
1.1 AMAPI SDK in Ihre Erweiterungs-App einbinden
Für die Verwaltung benutzerdefinierter Apps müssen Sie das AMAPI SDK in Ihre Erweiterungs-App einbinden. Weitere Informationen zu dieser Bibliothek und dazu, wie Sie sie Ihrer App hinzufügen, finden Sie im AMAPI SDK-Integrationsleitfaden.
1.2. Manifest Ihrer App aktualisieren, um FileProvider
zu unterstützen
- Fügen Sie Ihrer
AndroidManifest.xml
das<queries>
-Element für die Android Device Policy (ADP)-Anwendung hinzu, wie im AMAPI SDK-Integrationsleitfaden beschrieben. - Fügen Sie das folgende
<provider>
-Snippet in dasAndroidManifest.xml
Ihrer App innerhalb des<application>
-Tags ein. Mit diesem Snippet werden Dateien gespeichert, wenn das APK der benutzerdefinierten App freigegeben wird. So können benutzerdefinierte Apps mit der AMAPI installiert werden.
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>
- Erstellen Sie im Verzeichnis
res/xml/
Ihrer App eine neue XML-Datei, die den Speicherpfad für benutzerdefinierte APKs enthält.
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. Integration in die Funktion für benutzerdefinierte Apps des AMAPI SDK
2.1. Benutzerdefinierte APK-Datei für die Installation vorbereiten
Vor der Bereitstellung muss die APK-Datei der Anwendung für die Installation vorbereitet werden. Das folgende Code-Snippet veranschaulicht den Prozess:
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. Anfrage zum Installieren einer benutzerdefinierten App stellen
Das folgende Snippet zeigt, wie Sie eine Anfrage zum Installieren einer benutzerdefinierten App stellen:
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. Anfrage zum Abrufen installierter Apps stellen
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. Gerät mit benutzerdefinierten Richtlinien für die App-Verwaltung bereitstellen
Richten Sie ein
policy
mit den benutzerdefinierten Apps ein, die Sie verwalten möchten.{ "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" } } ] } ```
Erstellen Sie ein Registrierungstoken für das Gerät, indem Sie enterprises.enrollmentTokens.create mit
allowPersonalUsage
aufPERSONAL_USAGE_DISALLOWED
festlegen.Stellen Sie das Gerät mit dem Registrierungstoken im vollständig verwalteten Modus bereit.
Installieren Sie Ihre Erweiterbarkeits-App über Managed Google Play.
Ihre Erweiterbarkeits-App:
- kann die APK-Datei der benutzerdefinierten App herunterladen
- kann eine Anfrage zum Installieren der benutzerdefinierten App senden (siehe Code-Snippet oben).
- sollte eine Antwort erhalten
API
Server-Client-API
Beachten Sie die folgenden neuen Felder und Enums: