複数のユーザーを認識するアプリを構築する

デバイスが複数のユーザーをサポートしている場合、アプリはそれぞれのユーザーを認識する必要があります。

アプリによっては、いくつかのコンポーネントをシングルトンとして実行して、どのユーザーからのリクエストも受け付けられるようにする必要があります。現在、この機能を使用できるのはシステムアプリだけです。

この機能により、以下が実現します。

  • リソースを節約する
  • ユーザー間で 1 つ以上の共有リソースの使用を調整する
  • 単一のサーバー接続を使用してネットワークのオーバーヘッドを削減する

複数のユーザーをサポートしている場合の権限フローについて下の図をご覧ください。

複数のユーザーをサポートしている場合の権限フロー

図 1. 複数のユーザーをサポートしている場合の権限

シングルトン コンポーネントを有効にする

アプリをシングルトンとして指定するには、Android マニフェストのサービス、レシーバ、またはプロバイダに android:singleUser=”true” を追加します。

システムは、ユーザー 0 だけで実行されているプロセスで、そのコンポーネントをインスタンス化します。ユーザーからそのプロバイダまたはサービスに接続するリクエスト、あるいはそのレシーバにブロードキャストするリクエストは、ユーザー 0 のプロセスにルーティングされます。このコンポーネントがアプリ内の唯一のコンポーネントである場合、アプリのインスタンスが 1 つだけ実行されます。

パッケージ内のアクティビティは引き続き、各ユーザーの個別のプロセスで起動され、UID はそのユーザーの UID の範囲内(1010034 など)になります。

ユーザーとのインタラクション

権限を設定する

以下の権限が必要になります。

INTERACT_ACROSS_USERS (signature|system)
INTERACT_ACROSS_USERS_FULL (signature)

API を使用する

次の API を使用して、アプリが複数のユーザーを認識できるようにします。

  1. 受信した Binder 呼び出しからユーザー ハンドルを抽出します。
    • int userHandle = UserHandle.getCallingUserId()
  2. 保護されている新しい API を使用して、特定のユーザーでサービス、アクティビティ、ブロードキャストを開始します。
    • Context.startActivityAsUser(Intent, UserHandle)
    • Context.bindServiceAsUser(Intent, …, UserHandle)
    • Context.sendBroadcastAsUser(Intent, … , UserHandle)
    • Context.startServiceAsUser(Intent, …, UserHandle)
    UserHandle は、明示的なユーザーか、特殊なハンドルのいずれか(UserHandle.CURRENT または UserHandle.ALL)になります。CURRENT は、現在フォアグラウンドにあるユーザーを示します。すべてのユーザーにブロードキャストを送信する場合は、ALL を使用します。
  3. (INTERACT_ACROSS_USERS) で同じアプリ内のコンポーネントと通信します。他のアプリのコンポーネントと通信するには (INTERACT_ACROSS_USERS_FULL) を使用します。
  4. ユーザーのプロセスで実行されるプロキシ コンポーネントの作成が必要になる場合があります。このプロキシ コンポーネントは、ユーザー 0 の singleUser コンポーネントにアクセスします。
  5. 新しい UserManager システム サービスで、ユーザーとそのハンドルをクエリします。
    • UserManager.getUsers()
    • UserManager.getUserInfo()
    • UserManager.supportsMultipleUsers()
    • UserManager.getUserSerialNumber(int userHandle) - ユーザー ハンドルに対応する、リサイクルされない数値です。
    • UserManager.getUserHandle(int serialNumber)
    • UserManager.getUserProfiles() - 自己プロファイルと管理対象プロファイルのコレクションを返します(存在する場合)。
  6. コールバックの送信元ユーザーについての追加情報を提供する ContentObserver、PackageMonitor、BroadcastReceiver で新しい API を使用して、特定またはすべてのユーザー、およびコールバックをリッスンするように登録します。

複数のユーザーまたはプロファイルのサービス

すべてのサービスが、他のユーザーまたは仕事用プロファイルでインスタンスを実行する必要があるわけではありません。システム サービスをユーザー 0 として実行することのみが必要な場合は、リソースを保持できるように、他のユーザーでの実行時にサービスのコンポーネントを無効にします。次の例は、サービスのエントリ ポイントでコンポーネントを無効にする方法を示しています。

// Add on all entry points such as boot_completed or other manifest-listed receivers and providers
if (!UserManager.isSystemUser()) {
    // Disable the service
    ComponentName targetServiceName = new ComponentName(this, TargetService.class);
    context.getPackageManager().setComponentEnabledSetting(
        targetServiceName, COMPONENT_ENABLED_STATE_DISABLED, 0);
}

この例では、PackageManager.setApplicationEnabledSetting() を使用してアプリ全体を無効にすることもできます。