仕事用プロファイル

Android プラットフォームでは、デバイスに仕事用プロファイル(管理対象プロファイルとも呼ばれます)を設定できます。仕事用プロファイルは IT 管理者によって制御され、その機能はユーザーのプライマリ プロファイルの機能とは別に設定されます。このアプローチにより、組織はユーザーのデバイスで会社固有のアプリとデータが実行される環境を制御しながら、ユーザーに個人用アプリとプロファイルの使用を許可することができます。

このレッスンでは、仕事用プロファイルが設定されたデバイスで確実に動作するようにアプリを変更する方法について説明します。アプリ開発における通常のおすすめの方法以外の対応は必要ありません。ただし、これらのベスト プラクティスの一部は、仕事用プロファイルが設定されたデバイスで特に重要になります。このドキュメントでは、注意が必要な問題に焦点を当てています。

概要

多くの場合、ユーザーは個人所有のデバイスを企業環境で使用する。このような状況は、組織にジレンマをもたらす可能性があります。ユーザーが自分のデバイスを使用できる場合、組織は、機密情報(従業員のメールや連絡先など)が組織が管理していないデバイス上に存在することを懸念する必要があります。

この状況に対応するため、Android 5.0(API レベル 21)では、仕事用プロファイルを設定できます。デバイスに仕事用プロファイルがある場合、プロファイルの設定は IT 管理者によって管理されます。IT 管理者は、そのプロファイルで許可するアプリを選択し、プロファイルで使用できるデバイス機能のみを制御できます。

デバイスに仕事用プロファイルが設定されている場合、アプリがどのプロファイルで実行されているかにかかわらず、デバイスで実行されるアプリに影響します。

  • デフォルトでは、ほとんどのインテントが一方のプロファイルから他方のプロファイルにまたがることはありません。プロファイルで実行されているアプリがインテントを起動した場合、そのプロファイルにインテントのハンドラがなく、プロファイルの制限によりインテントが他のプロファイルに渡されない場合、リクエストは失敗し、アプリが予期せずシャットダウンする可能性があります。
  • プロファイルの IT 管理者は、仕事用プロファイルで使用できるシステムアプリを制限できます。この制限により、仕事用プロファイルで一部の一般的なインテントにハンドラがなくなることもあります。
  • 個人用プロファイルと仕事用プロファイルのストレージ領域が別々になっているため、一方のプロファイルで有効なファイル URI が他方のプロファイルでは有効になりません。一方のプロファイルで起動されたインテントは、(プロファイルの設定に応じて)もう一方のプロファイルで処理される場合があるため、ファイル URI をインテントにアタッチすることは安全ではありません。

インテントの失敗を防ぐ

仕事用プロファイルが設定されたデバイスでは、インテント間でプロファイルを切り替えられるかどうかに制限があります。ほとんどの場合、インテントは起動時と同じプロファイルで処理されます。そのプロファイルにインテントのハンドラがない場合、インテントは処理されず、他のプロファイルにインテントのハンドラがあっても、それを呼び出したアプリが予期せずシャットダウンする可能性があります。

プロファイル管理者は、あるプロファイルから別のプロファイルに交差できるインテントを選択できます。この処理は IT 管理者が行うため、この境界を越えることができるインテントを事前に把握することはできません。IT 管理者はこのポリシーを設定し、いつでも変更できます。

アプリがアクティビティを開始する前に、適切な解像度があることを確認する必要があります。許容可能な解像度が存在するかどうかを確認するには、Intent.resolveActivity() を呼び出します。インテントを解決する方法がない場合、メソッドは null を返します。メソッドが null 以外の値を返した場合、インテントを解決する方法は少なくとも 1 つあり、安全にインテントを起動できます。この場合、現在のプロファイルにハンドラが存在するため、または、インテントが他のプロファイルのハンドラに渡せるため、インテントが解決される場合があります。(インテントの解決の詳細については、一般的なインテントをご覧ください)。

たとえば、アプリでタイマーを設定する必要がある場合、ACTION_SET_TIMER インテントに有効なハンドラが存在することを確認する必要があります。アプリがインテントを解決できない場合は、適切なアクション(エラー メッセージの表示など)を行う必要があります。

Kotlin

fun startTimer(message: String, seconds: Int) {

    // Build the "set timer" intent
    val timerIntent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(packageManager) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent)

    }
}

Java

public void startTimer(String message, int seconds) {

    // Build the "set timer" intent
    Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(getPackageManager()) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent);

    }
}

プロファイル間でファイルを共有する

アプリが、他のアプリに自身のファイルへのアクセスを提供する必要が生じることがあります。たとえば、画像ギャラリー アプリで、画像を画像エディタと共有したい場合があります。通常、ファイルを共有するには、ファイル URI とコンテンツ URI の 2 つの方法があります。

ファイル URI は file: 接頭辞で始まり、その後にデバイスのストレージにあるファイルの絶対パスが続きます。ただし、仕事用プロファイルと個人用プロファイルは別々のストレージ領域を使用するため、一方のプロファイルで有効なファイル URI が他方のプロファイルでは有効になりません。つまり、ファイルの URI をインテントに添付した場合、そのインテントが他のプロファイルで処理されると、ハンドラはファイルにアクセスできません。

代わりに、コンテンツ URI を使用してファイルを共有する必要があります。コンテンツ URI は、より安全で共有可能な方法でファイルを識別します。コンテンツ URI には、ファイルパスだけでなく、ファイルを提供する権限と、ファイルを識別する ID 番号も含まれます。任意のファイルのコンテンツ ID を生成するには、FileProvider を使用します。そのコンテンツ ID は他のアプリ(別のプロファイルであっても)と共有できます。受信者は、コンテンツ ID を使用して実際のファイルにアクセスできます。

たとえば、特定のファイル URI のコンテンツ URI を取得する方法を次に示します。

Kotlin

// Open File object from its file URI
val fileToShare = File(fileUriToShare)

val contentUriToShare: Uri = FileProvider.getUriForFile(
        context,
        "com.example.myapp.fileprovider",
        fileToShare
)

Java

// Open File object from its file URI
File fileToShare = new File(fileUriToShare);

Uri contentUriToShare = FileProvider.getUriForFile(getContext(),
        "com.example.myapp.fileprovider", fileToShare);

getUriForFile() メソッドを呼び出すときは、ファイル プロバイダの権限(この例では "com.example.myapp.fileprovider")を含める必要があります。これはアプリ マニフェストの <provider> 要素で指定されています。コンテンツ URI を使用したファイルの共有について詳しくは、ファイルの共有をご覧ください。

通知をリッスンする

アプリは通常、通知の変更に関するコールバックをシステムから受け取る NotificationListenerService サブクラスを提供します。仕事用プロファイルが設定されたデバイスは、アプリとの NotificationListenerService の動作に影響を与える可能性があります。

仕事用プロファイルの場合

仕事用プロファイルで実行されているアプリから NotificationListenerService を使用することはできません。アプリが仕事用プロファイルで実行されている場合、システムはアプリの NotificationListenerService を無視します。ただし、個人用プロファイルで実行されているアプリは通知をリッスンできます。

個人用プロファイルの場合

アプリが個人用プロファイルで実行されている場合、仕事用プロファイルで実行されているアプリの通知が届かないことがあります。デフォルトでは、すべての個人用プロファイル アプリがコールバックを受け取りますが、IT 管理者は 1 つ以上の個人用プロファイル アプリを許可リストに登録して、通知の変更をリッスンできます。許可リストに登録されていないアプリはブロックされます。Android 8.0(API レベル 26)以降では、仕事用プロファイルを管理する Device Policy Controller(DPC)によって、アプリが DevicePolicyManager メソッド setPermittedCrossProfileNotificationListeners() を使用して仕事用プロファイルの通知をリッスンできないことがあります。アプリは、個人用プロファイルに投稿された通知に関するコールバックを引き続き受け取ります。

アプリと仕事用プロファイルとの互換性をテストする

仕事用プロファイル環境でアプリをテストし、仕事用プロファイルが設定されたデバイスでアプリのエラーを引き起こす可能性のある問題を検出する必要があります。特に、仕事用プロファイル デバイスでテストすると、処理できないインテントを発行しない、クロス プロファイルで動作しない URI をアタッチしないなど、アプリがインテントを適切に処理できるかを確認するのに適しています。

Android 5.0(API レベル 21)以降を搭載している Android デバイスで仕事用プロファイルをセットアップできるサンプルアプリ TestDPC も用意されています。このアプリでは、仕事用プロファイル環境でアプリを簡単にテストできます。また、このアプリを使用して、次のように仕事用プロファイルを構成することもできます。

  • 管理対象プロファイルで使用可能なデフォルト アプリを指定する
  • 一方のプロファイルから他方のプロファイルへの交差を許可するインテントを構成します

仕事用プロファイルが設定されたデバイスに、USB ケーブル経由でアプリを手動でインストールすると、アプリは個人用プロファイルと仕事用プロファイルの両方にインストールされます。アプリをインストールしたら、次の条件でアプリをテストできます。

  • インテントが通常はデフォルト アプリ(カメラアプリなど)によって処理される場合は、仕事用プロファイルでそのデフォルト アプリを無効にし、アプリがこれを適切に処理することを確認してください。
  • インテントが他のアプリによって処理されることを想定している場合は、プロファイル間でクロスするインテントの権限の有効と無効を切り替えてみてください。どちらの状況でもアプリが正しく動作することを確認します。インテントがプロファイル間で交差することが許可されている場合は、アプリのプロファイルに適切なハンドラが存在する場合と存在しない場合の両方で、アプリの動作を検証します。たとえば、アプリが地図関連のインテントを起動する場合は、以下の各シナリオを試してください。
    • デバイスで、あるプロファイルから別のプロファイルにインテントをマッピングでき、別のプロファイル(アプリが実行されていないプロファイル)に適切なハンドラが存在すること。
    • デバイスで、マップ インテントをプロファイル間で交差できないが、アプリのプロファイルに適切なハンドラがある
    • デバイスで、プロファイル間でマッピング インテントを交差することができず、デバイスのプロファイルにマップ インテントに適したハンドラがない
  • インテントにコンテンツをアタッチする場合は、アプリのプロファイルで処理されるときと、プロファイルをまたがるときに、インテントが適切に動作することを確認します。

仕事用プロファイルでのテスト: ヒントとコツ

仕事用プロファイルのデバイスでテストする際に役立つコツがいくつかあります。

  • 前述のように、仕事用プロファイル デバイスでアプリをサイドローディングすると、両方のプロファイルにインストールされます。必要に応じて、一方のプロファイルからアプリを削除し、もう一方のプロファイルには残すこともできます。
  • Android Debug Bridge(adb)シェルで利用可能なアクティビティ マネージャー コマンドのほとんどは、--user フラグをサポートしています。これにより、実行するユーザーを指定できます。ユーザーを指定すると、管理対象外のプライマリ ユーザーとして実行するか、仕事用プロファイルとして実行するかを選択できます。詳しくは、ADB シェル コマンドをご覧ください。
  • デバイス上のアクティブ ユーザーを検索するには、adb パッケージ マネージャーの list users コマンドを使用します。出力文字列の最初の数字がユーザー ID で、--user フラグで使用できます。詳細については、ADB シェルコマンドをご覧ください。

たとえば、デバイス内のユーザーを見つけるには、次のコマンドを実行します。

$ adb shell pm list users
UserInfo{0:Drew:13} running
UserInfo{10:Work profile:30} running

この場合、プライマリ ユーザー(「Drew」)のユーザー ID は 0、仕事用プロファイルのユーザー ID は 10 です。仕事用プロファイルでアプリを実行するには、次のようなコマンドを使用します。

$ adb shell am start --user 10 \
-n "com.example.myapp/com.example.myapp.testactivity" \
-a android.intent.action.MAIN -c android.intent.category.LAUNCHER