AMAPI SDK との統合

AMAPI SDK を使用すると、EMM 指定の拡張機能アプリが Android Device Policy と直接通信できるようになります。現在、Commands のローカル実行と ClearAppData コマンドのみがサポートされています。SDK と統合するには、次の操作を行う必要があります。

  1. 拡張機能アプリにライブラリを追加します。
  2. 用意されている API を使用し、必要に応じてコマンドを発行します。
  3. クエリ要素を追加(ターゲット SDK が 30 以上の場合)。
  4. 必要に応じて、コマンド ステータス変更のコールバックをリッスンするサービスの実装を指定できます。
  5. 拡張ポリシーを使用してデバイスをプロビジョニングします。

前提条件

  • 拡張機能アプリの minSdkVersion が API レベル 21 以降に設定されていることを確認します。

拡張機能アプリへのライブラリの追加

トップレベルの build.gradle ファイルで、SDK ライブラリを含む Google Maven リポジトリを関連するモジュールに追加し、依存関係をライブラリに追加します。

repositories {
  ...
  google()
}

次に、モジュールの dependencies ブロックにライブラリを追加します。

dependencies {
  implementation 'com.google.android.libraries.enterprise.amapi:amapi:1.0.0'
}

Android Device Policy にリクエストを送信する

これで、ADP にリクエストを送信できるようになりました。次のリクエストがサポートされています。

コマンドを発行

拡張機能アプリは、ADP を使用してコマンドを発行するようにリクエストできます。IssueCommandRequest には、発行するコマンドの詳細と特定のパラメータを含むリクエスト オブジェクトが含まれます。詳細については、Javadoc をご覧ください。

次のスニペットは、パッケージのデータの消去をリクエストする方法を示しています。

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;

...
  void issueClearAppDataCommand(ImmutableList<String> packageNames) {
    Futures.addCallback(
        LocalCommandClientFactory.create(getContext())
            .issueCommand(createClearAppRequest(packageNames)),
        new FutureCallback<Command>() {
          @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 createClearAppRequest(ImmutableList<String> packageNames) {
    return IssueCommandRequest.builder()
        .setClearAppsData(
            ClearAppsData.builder()
                .setPackageNames(packageNames)
                .build()
        )
        .build();
  }
...

上記の例では、指定したパッケージに対してアプリデータの消去リクエストを発行し、コマンドが正常に発行されるまで待機しています。正常に発行されると、コマンド オブジェクトが現在のコマンド ステータスとコマンド ID とともに返されます。後で、この ID を使用して、長時間実行コマンドのステータスをクエリできます。

コマンドを取得

拡張機能アプリは、以前に発行されたコマンド リクエストのステータスをクエリすることもできます。コマンドのステータスを取得するには、コマンド ID(発行コマンド リクエストから入手可能)が必要です。次のスニペットは、ADP に GetCommand リクエストを送信する方法を示しています。

import android.util.Log;
...
import com.google.android.managementapi.commands.LocalCommandClientFactory;
...
import com.google.android.managementapi.commands.model.GetCommandRequest;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;

...
  void getCommand(String commandId) {
    Futures.addCallback(
        LocalCommandClientFactory.create(getApplication())
            .getCommand(GetCommandRequest.builder().setCommandId(commandId).build()),
        new FutureCallback<Command>() {
          @Override
          public void onSuccess(Command result) {
            // Process the returned command result here
            Log.i(Constants.TAG, "Successfully issued command");
          }

          @Override
          public void onFailure(Throwable t) {
            Log.e(Constants.TAG, "Failed to issue command", t);
          }
        },
        MoreExecutors.directExecutor());
  }
  ...

クエリ要素を追加しています

アプリが SDK 30 以降をターゲットとしている場合は、ADP とやり取りすることを指定するためにマニフェストにクエリ要素が必要です。

<queries>
    <package android:name="com.google.android.apps.work.clouddpc" />
</queries>

詳しくは、Android でのパッケージの公開設定のフィルタリング をご覧ください。

コマンド ステータス変更のコールバックをリッスンする

  1. コマンド ステータスの変更は CommandListener に通知されます。このインターフェースをアプリに実装して、受信したステータス更新を処理する方法を実装します。
  2. NotificationReceiverService を拡張して CommandListener インスタンスを提供します。
  3. Android Management API ポリシーで拡張 NotificationReceiverService のクラス名を指定します(ポリシーの構成を参照)。

    import com.google.android.managementapi.commands.CommandListener;
    import com.google.android.managementapi.notification.NotificationReceiverService;
    
    ...
    
    public class SampleCommandService extends NotificationReceiverService {
    
      @Override
      protected void setupInjection() {
        // (Optional) If using DI and needs initialisation then use this method.
      }
    
      @Override
      public CommandListener getCommandListener() {
        // return the concrete implementation from previous step
        return ...;
      }
    }
    
  4. AndroidManifest.xml にサービスを追加して、エクスポートされていることを確認します。

    <service
     android:name = ".notification.SampleCommandService"
     android:exported = "true" />
    

ポリシーの構成

 "applications": [{
   "packageName": "com.amapi.extensibility.demo",
   ...
   "extensionConfig": {
     "signingKeyFingerprintsSha256": [
       // Include signing key of extension app
     ],
     // Optional if callback is implemented
     "notificationReceiver": "com.amapi.extensibility.demo.notification.SampleCommandService"
   }
 }]

テスト

単体テスト

LocalCommandClient はインターフェースであるため、テストではテスト可能な実装を簡単に提供できます。

統合テスト

Android Device Policy を使用してテストするには、次の情報が必要です。

  1. 拡張機能アプリのパッケージ名。
  2. アプリ パッケージに関連付けられている署名の 16 進数でエンコードされた SHA-256 ハッシュ。
  3. (省略可)コールバックをテストする場合。コールバックをサポートする、新しく導入されたサービスの完全修飾名。(この例では CommandService の完全修飾名)。