管理対象設定のセットアップ

エンタープライズ市場向けのアプリを開発する場合は、組織のポリシーで定められた特定の要件を満たす必要があります。管理対象設定(旧称はアプリケーション制限)を使用すると、組織の IT 管理者はリモートでアプリの設定を指定できます。この機能は、仕事用プロファイルにデプロイされた組織の承認済みアプリで特に役立ちます。

たとえば、承認済みのアプリでは、IT 管理者が次のことを行えるようにしなければならない場合があります。

  • ウェブブラウザの URL を許可またはブロックする
  • アプリがモバイル デバイス経由でのコンテンツ同期を許可するか、Wi-Fi 経由の同期のみを許可するかを設定する
  • アプリのメール設定を構成する

このガイドでは、アプリに管理対象設定を実装する方法について説明します。管理対象設定を使用してサンプルアプリを表示するには、ManagedConfigurations をご覧ください。企業向けモバイル管理(EMM)のデベロッパーの方は、Android Management API ガイドをご覧ください。

注: 歴史上の理由から、こうした構成設定は「制限」と呼ばれ、この用語を使用するファイルとクラス(RestrictionsManager など)とともに実装されます。しかし、実際にこうした制限は、アプリの機能の制限だけでなく、さまざまな構成オプションを実装することもできます。

リモート構成の概要

アプリでは、IT 管理者がリモートで設定できる管理対象構成オプションを定義します。これらは任意の設定であり、マネージド構成プロバイダが変更できます。アプリが仕事用プロファイルで実行されている場合、IT 管理者はアプリの管理対象設定を変更できます。

管理対象設定プロバイダは、同じデバイスで実行されている別のアプリです。 通常、このアプリは IT 管理者によって管理されています。IT 管理者は、構成の変更を管理対象構成プロバイダ アプリに伝えます。次に、そのアプリはアプリの構成を変更します。

外部で管理する構成を提供するには:

  • アプリ マニフェストで管理対象の設定を宣言します。これにより、IT 管理者は Google Play API を介してアプリの設定を読み取れるようになります。
  • アプリが再開するたびに、RestrictionsManager オブジェクトを使用して現在の管理対象構成をチェックし、その構成に適合するようにアプリの UI と動作を変更します。
  • ACTION_APPLICATION_RESTRICTIONS_CHANGED インテントをリッスンします。このブロードキャストを受信したら、RestrictionsManager をチェックして現在の管理対象設定を確認し、アプリの動作に必要な変更を加えます。

管理対象構成を定義する

アプリは、定義する管理対象設定をサポートできます。アプリの管理対象構成を管理対象構成ファイルで宣言し、構成ファイルをマニフェストで宣言します。構成ファイルを作成すると、他のアプリは、そのアプリが提供する管理対象構成を検証できます。EMM パートナーは、Google Play API を使用してアプリの設定を読み取ることができます。

アプリのリモート設定オプションを定義するには、マニフェストの <application> 要素に次の要素を追加します。

<meta-data android:name="android.content.APP_RESTRICTIONS"
    android:resource="@xml/app_restrictions" />

アプリの res/xml ディレクトリに app_restrictions.xml という名前のファイルを作成します。このファイルの構造については、RestrictionsManager のリファレンスをご覧ください。このファイルには、最上位の <restrictions> 要素が 1 つ含まれています。この要素には、アプリの設定オプションごとに 1 つの <restriction> 子要素が含まれます。

注: 管理対象構成ファイルのローカライズ版は作成しないでください。アプリが保持できる管理対象構成ファイルは 1 つのみであるため、すべての言語 / 地域で構成に一貫性を持たせます。

企業環境では、EMM は通常、管理対象構成スキーマを使用して IT 管理者用のリモート コンソールを生成するため、管理者はリモートでアプリケーションを構成できます。

管理対象構成プロバイダは、アプリにクエリを実行して、アプリの使用可能な構成の詳細(説明テキストなど)を確認できます。設定プロバイダと IT 管理者は、アプリが実行されていない場合でも、アプリの管理対象設定をいつでも変更できます。

たとえば、モバイル接続を介したデータのダウンロードを許可または禁止するように、アプリをリモートで構成できるとします。アプリには次のような <restriction> 要素を使用できます。

<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">

  <restriction
    android:key="downloadOnCellular"
    android:title="@string/download_on_cell_title"
    android:restrictionType="bool"
    android:description="@string/download_on_cell_description"
    android:defaultValue="true" />

</restrictions>

各構成の android:key 属性を使用して、マネージド構成バンドルからその値を読み取ります。このため、各設定には一意のキー文字列が必要であり、その文字列はローカライズできません。文字列リテラルで指定する必要があります。

注: 製品版アプリでは、android:titleandroid:description は、リソースを使用したローカライズで説明されているように、ローカライズされたリソース ファイルから取得する必要があります。

アプリは bundle_array 内のバンドルを使用して制限を定義します。たとえば、複数の VPN 接続オプションがあるアプリでは、各 VPN サーバー構成を bundle で定義し、複数のバンドルをバンドル配列にまとめます。

<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android" >

  <restriction
    android:key="vpn_configuration_list"
    android:restrictionType="bundle_array">
    <restriction
      android:key="vpn_configuration"
      android:restrictionType="bundle">
      <restriction
        android:key="vpn_server"
        android:restrictionType="string"/>
      <restriction
        android:key="vpn_username"
        android:restrictionType="string"/>
      <restriction
        android:key="vpn_password"
        android:restrictionType="string"/>
    </restriction>
  </restriction>

</restrictions>

android:restrictionType 要素でサポートされる型を表 1 に示します。また、RestrictionsManagerRestrictionEntry のリファレンスにも記載されています。

表 1. 制限エントリのタイプと使用方法。

タイプ android:restrictionType 一般的な使用方法
TYPE_BOOLEAN "bool" ブール値(true または false)。
TYPE_STRING "string" 名前などの文字列値。
TYPE_INTEGER "integer" MIN_VALUEMAX_VALUE の値を持つ整数。
TYPE_CHOICE "choice" android:entryValues から選択された文字列値。通常は単一選択リストとして表示されます。
TYPE_MULTI_SELECT "multi-select" android:entryValues から選択された値を含む文字列配列。 複数のエントリを選択できる複数選択リストを表示する場合に使用します。たとえば、許可リストに登録する特定のタイトルを選択する場合に使用します。
TYPE_NULL "hidden" 非表示の制限タイプ。このタイプは、転送する必要があるが UI でユーザーに表示すべきでない情報に使用します。単一の文字列値を保存します。
TYPE_BUNDLE_ARRAY "bundle_array" 制限 bundles の配列を格納するために使用します。Android 6.0(API レベル 23)で使用できます。

注: android:entryValues は機械で読み取り可能であり、ローカライズできません。android:entries を使用して、ローカライズ可能な人が読める値を表示します。各エントリは、対応するインデックスを android:entryValues に持つ必要があります。

管理対象設定を確認する

他のアプリが構成設定を変更しても、アプリに自動的に通知されることはありません。代わりに、アプリの起動時または再開時の管理対象構成を確認し、システム インテントをリッスンして、アプリの実行中に構成が変更されたかどうかを検出する必要があります。

現在の構成設定を確認するには、アプリで RestrictionsManager オブジェクトを使用します。アプリは、以下のタイミングで現在の管理対象設定を確認する必要があります。

RestrictionsManager オブジェクトを取得するには、getActivity() で現在のアクティビティを取得し、そのアクティビティの Activity.getSystemService() メソッドを呼び出します。

Kotlin

var myRestrictionsMgr =
        activity?.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager

Java

RestrictionsManager myRestrictionsMgr =
    (RestrictionsManager) getActivity()
        .getSystemService(Context.RESTRICTIONS_SERVICE);

RestrictionsManager を作成したら、getApplicationRestrictions() メソッドを呼び出して現在の構成設定を取得できます。

Kotlin

var appRestrictions: Bundle = myRestrictionsMgr.applicationRestrictions

Java

Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();

注: 便宜上、UserManager.getApplicationRestrictions() を呼び出して UserManager で現在の構成を取得することもできます。このメソッドは RestrictionsManager.getApplicationRestrictions() とまったく同じように動作します。

getApplicationRestrictions() メソッドはデータ ストレージから読み取る必要があるため、読み取りは控えめに行う必要があります。現在の構成を知る必要があるたびにこのメソッドを呼び出さないでください。代わりに、アプリの起動時または再開時に 1 回呼び出し、取得した管理対象設定バンドルをキャッシュに保存してください。次に、管理対象の構成の変更をリッスンするで説明されているように、ACTION_APPLICATION_RESTRICTIONS_CHANGED インテントをリッスンして、アプリがアクティブな間に構成が変更されたかどうかを確認します。

管理対象構成の読み取りと適用

getApplicationRestrictions() メソッドは、設定されている各構成の Key-Value ペアを含む Bundle を返します。値はすべて BooleanintStringString[] 型です。管理対象構成 Bundle を取得したら、getBoolean()getString() など、これらのデータ型用の標準の Bundle メソッドで現在の構成設定を確認できます。

注: 管理対象構成 Bundle には、管理対象構成プロバイダが明示的に設定したすべての構成項目が 1 つずつ含まれています。ただし、マネージド構成の XML ファイルでデフォルト値を定義したからといって、バンドル内に構成が存在するとは限りません。

現在の管理対象構成の設定に基づいて、アプリが適切に処理する必要があります。たとえば、アプリにモバイル接続を介してデータをダウンロードできるかどうかを指定する設定があり、その設定が false になっている場合は、次のサンプルコードに示すように、デバイスが Wi-Fi 接続を利用している場合を除き、データのダウンロードを無効にする必要があります。

Kotlin

val appCanUseCellular: Boolean =
        if (appRestrictions.containsKey("downloadOnCellular")) {
            appRestrictions.getBoolean("downloadOnCellular")
        } else {
            // cellularDefault is a boolean using the restriction's default value
            cellularDefault
        }

if (!appCanUseCellular) {
    // ...turn off app's cellular-download functionality
    // ...show appropriate notices to user
}

Java

boolean appCanUseCellular;

if (appRestrictions.containsKey("downloadOnCellular")) {
    appCanUseCellular = appRestrictions.getBoolean("downloadOnCellular");
} else {
    // cellularDefault is a boolean using the restriction's default value
    appCanUseCellular = cellularDefault;
}

if (!appCanUseCellular) {
    // ...turn off app's cellular-download functionality
    // ...show appropriate notices to user
}

複数のネストされた制限を適用するには、bundle_array 制限エントリを Parcelable オブジェクトのコレクションとして読み取り、Bundle としてキャストします。この例では、各 VPN の構成データが解析されて、サーバー接続の選択肢のリストが作成されます。

Kotlin

// VpnConfig is a sample class used store config data, not defined
val vpnConfigs = mutableListOf<VpnConfig>()

val parcelables: Array<out Parcelable>? =
        appRestrictions.getParcelableArray("vpn_configuration_list")

if (parcelables?.isNotEmpty() == true) {
    // iterate parcelables and cast as bundle
    parcelables.map { it as Bundle }.forEach { vpnConfigBundle ->
        // parse bundle data and store in VpnConfig array
        vpnConfigs.add(VpnConfig()
                .setServer(vpnConfigBundle.getString("vpn_server"))
                .setUsername(vpnConfigBundle.getString("vpn_username"))
                .setPassword(vpnConfigBundle.getString("vpn_password")))
    }
}

if (vpnConfigs.isNotEmpty()) {
    // ...choose a VPN configuration or prompt user to select from list
}

Java

// VpnConfig is a sample class used store config data, not defined
List<VpnConfig> vpnConfigs = new ArrayList<>();

Parcelable[] parcelables =
    appRestrictions.getParcelableArray("vpn_configuration_list");

if (parcelables != null && parcelables.length > 0) {
    // iterate parcelables and cast as bundle
    for (int i = 0; i < parcelables.length; i++) {
        Bundle vpnConfigBundle = (Bundle) parcelables[i];
        // parse bundle data and store in VpnConfig array
        vpnConfigs.add(new VpnConfig()
            .setServer(vpnConfigBundle.getString("vpn_server"))
            .setUsername(vpnConfigBundle.getString("vpn_username"))
            .setPassword(vpnConfigBundle.getString("vpn_password")));
    }
}

if (!vpnConfigs.isEmpty()) {
    // ...choose a VPN configuration or prompt user to select from list
}

マネージド構成の変更をリッスンする

アプリの管理対象構成が変更されるたびに、システムによって ACTION_APPLICATION_RESTRICTIONS_CHANGED インテントが発行されます。構成設定が変更されたときにアプリの動作を変更できるように、アプリはこのインテントをリッスンする必要があります。

注: ACTION_APPLICATION_RESTRICTIONS_CHANGED インテントは、動的に登録されたリスナーにのみ送信され、アプリ マニフェストで宣言されたリスナーには送信されません。

次のコードは、このインテントにブロードキャスト レシーバを動的に登録する方法を示しています。

Kotlin

val restrictionsFilter = IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)

val restrictionsReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {

        // Get the current configuration bundle
        val appRestrictions = myRestrictionsMgr.applicationRestrictions

        // Check current configuration settings, change your app's UI and
        // functionality as necessary.
    }
}

registerReceiver(restrictionsReceiver, restrictionsFilter)

Java

IntentFilter restrictionsFilter =
    new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);

BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
  @Override public void onReceive(Context context, Intent intent) {

    // Get the current configuration bundle
    Bundle appRestrictions = myRestrictionsMgr.getApplicationRestrictions();

    // Check current configuration settings, change your app's UI and
    // functionality as necessary.
  }
};

registerReceiver(restrictionsReceiver, restrictionsFilter);

注: 通常、アプリは一時停止時に構成の変更について通知する必要はありません。代わりに、アプリが一時停止したときにブロードキャスト レシーバの登録を解除する必要があります。アプリが再開したら、まず現在の管理対象設定をチェックし(管理対象設定の確認を参照)、ブロードキャスト レシーバを登録して、アプリがアクティブな間に発生した構成変更について通知を受け取れるようにします。

管理対象設定のフィードバックを EMM に送信する

管理対象の構成変更をアプリに適用した後は、EMM に変更のステータスを通知することをおすすめします。Android は「キーによるアプリの状態」という機能をサポートしています。この機能を使用して、管理対象の構成変更をアプリが適用しようとするたびにフィードバックを送信できます。このフィードバックは、アプリが管理対象構成を正常に設定したことを確認するものとして役立ちます。また、アプリが指定の変更を適用できなかった場合は、エラー メッセージとして表示されることもあります。

EMM プロバイダは、このフィードバックを取得してコンソールに表示し、IT 管理者が確認できます。このトピックの詳細については、EMM にアプリのフィードバックを送信するをご覧ください(アプリにフィードバック サポートを追加する方法に関する詳細なガイドなど)。

他のサンプルコード

ManagedConfigurations のサンプルでは、このページで取り上げた API の使用方法が詳しく説明されています。