Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

クイック アクセス ウォレット

Android 11 で導入されたクイック アクセス ウォレット機能を使用すると、ユーザーは電源ボタンメニューから支払いカードと関連パスに直接アクセスできます。主なユースケースとしては、NFC デバイスで取引を実行する前に該当の支払い方法を選択する場合や、今後のイベントのフライトなどのパスにすばやくアクセスする場合があります。

電源ボタンメニューのクイック アクセス ウォレット機能
図 1. 電源ボタンメニューのクイック アクセス ウォレット機能

要件

クイック アクセス ウォレット機能を使用するには、デバイスに NFC が搭載されていることが必要です。この機能は、デフォルトの NFC 支払いアプリの QuickAccessWalletService にバインドします。つまり、デバイスは NFC ホストベースのカード エミュレーション(HCE)もサポートする必要があります。

機能の概要

クイック アクセス ウォレットには 2 つの部分があります。それは、クイック アクセス ウォレット UI とクイック アクセス ウォレット カード プロバイダです。

ウォレット UI はシステム UI 内で実行され、platform/packages/apps/QuickAccessWallet に実装されます。ウォレット UI を電源ボタンメニューに表示するには、パッケージを組み込んでホワイトリストに登録し、クイック アクセス ウォレット カード プロバイダをインストールする以外の変更は必要ありません。

クイック アクセス ウォレット カード プロバイダは、デフォルトの NFC 支払いアプリです。ユーザーは複数の NFC 支払いアプリを同時にインストールできますが、電源ボタンメニューにカードを表示できるのはデフォルトの NFC 支払いアプリだけです。デフォルトに設定する NFC 支払いアプリは出荷前に指定できますが、ユーザーは設定ページで別のアプリを選択できます。インストールされた NFC 支払いアプリが 1 つのみである場合は、そのアプリが自動的にデフォルトになります(CardEmulationManager を参照)。

実装

クイック アクセス ウォレット UI にカードを提供するには、NFC 支払いアプリに QuickAccessWalletService を実装する必要があります。支払いアプリには、サービスをアドバタイズするマニフェスト エントリを含める必要があります。

システム UI のみが QuickAccessWalletService にバインドできるようにするには、NFC 支払いアプリが android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE 権限を要求する必要があります。この権限を要求することにより、システム UI のみが QuickAccessWalletService にバインドできるようになります。

<service
     android:name=".MyQuickAccessWalletService"
     android:label="@string/my_default_tile_label"
     android:icon="@drawable/my_default_icon_label"
     android:logo="@drawable/my_wallet_logo"
     android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
     <intent-filter>
         <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
         <category android:name="android.intent.category.DEFAULT"/>
     </intent-filter>
     <meta-data android:name="android.quickaccesswallet"
          android:resource="@xml/quickaccesswallet_configuration" />;
 </service>

ウォレットに関する追加情報は、リンク先の XML ファイルに含まれています。

<quickaccesswallet-service
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:settingsActivity="com.example.android.SettingsActivity"
   android:shortcutLongLabel="@string/my_wallet_empty_state_text"
   android:shortcutShortLabel="@string/my_wallet_button_text"
   android:targetActivity="com.example.android.WalletActivity"/>

次に、支払いアプリに QuickAccessWalletService を実装する必要があります。

public class MyQuickAccessWalletService extends QuickAccessWalletService {

    @Override
    public void onWalletCardsRequested(
            GetWalletCardsRequest request,
            GetWalletCardsCallback callback) {
        GetWalletCardsResponse response = // generate response
        callback.onSuccess(response);
    }

    @Override
    public void onWalletCardSelected(SelectWalletCardRequest request) {
        // selecting a card should ensure that it is used when making an NFC payment
    }

    @Override
    public void onWalletDismissed() {
        // May un-select card if the wallet app has the concept of a 'default'
        // payment method
    }
}

HostApduService が NFC トランザクションの処理を開始し、その結果として支払いの進捗状況と結果を表示するアクティビティが開始されたら、バインドされた QuickAccessWalletService への参照を取得し、イベントタイプ TYPE_NFC_PAYMENT_STARTED を使用して QuickAccessWalletService#sendEvent を呼び出す必要もあります。それによってクイック アクセス ウォレット UI が閉じられ、ユーザーは UI に遮られずに支払いアクティビティを見ることができます。

QuickAccessWalletService の実装方法に関するその他のドキュメントとしては、QuickAccessWalletService および TestQuickAccessWalletService CTS テストをご覧ください。

クイック アクセス ウォレット UI の有効化

電源ボタンメニューからクイック アクセス ウォレットを利用できるように構成するには、ビルドに QuickAccessWallet ターゲットを組み込み、下記のコードサンプルの太字の行を overlay/frameworks/base/packages/SystemUI/res/values/config.xml ファイルに追加して globalactions.wallet プラグインを有効にします。

<resources>
    ...
    <!-- SystemUI Plugins that can be loaded on user builds. -->
    <string-array name="config_pluginWhitelist" translatable="false">
        <item>com.android.systemui</item>
        <item>com.android.systemui.plugin.globalactions.wallet</item>
    </string-array>
</resources>

def_nfc_payment_component を使用して、設定の構成ファイルにデフォルトの NFC 支払いアプリを指定します。

クイック アクセス ウォレットにカードを提供するには、デフォルトの NFC 支払いアプリが QuickAccessWalletService を公開する必要があります。デフォルトの NFC 支払いアプリがこのサービスをエクスポートしないと、ウォレット UI は表示されません。

QuickAccessWalletService 実装の詳細

QuickAccessWalletService に、onWalletCardsRequestedonWalletCardSelectedonWalletDismissed の 3 つの抽象メソッドを実装する必要があります。下記のシーケンス図は、クイック アクセス ウォレットが NFC 支払いの直前に表示されるときの呼び出しシーケンスを示しています。

クイック アクセス ウォレットのシーケンス図

クイック アクセス ウォレットが表示されるときの呼び出しシーケンスの例
図 2. クイック アクセス ウォレットが表示されるときの呼び出しシーケンスの例

クイック アクセス ウォレットの表示後に常に NFC 支払いが行われるわけではありませんが、上記の図 2 は QuickAccessWalletService のすべての機能を示しています。この例では、クイック アクセス ウォレット カード プロバイダは青い枠で囲まれた要素を実装しています。支払いカードはデバイス内のデータベースに保存され、PaymentCardManager という名前のインターフェースを介してアクセスされると想定しています。さらに、PaymentActivity という名前のアクティビティによって NFC 支払いの結果が表示されると想定しています。フローは次のように進行します。

  1. ユーザーがクイック アクセス ウォレットを表示するジェスチャーを行います。
  2. クイック アクセス ウォレット UI(システム UI の一部)がパッケージ マネージャーをチェックし、デフォルトの NFC 支払いアプリが QuickAccessWalletService をエクスポートするかどうかを確認します。

    • サービスがエクスポートされなければ、クイック アクセス ウォレットは表示されません。
  3. クイック アクセス ウォレット UI が QuickAccessWalletService にバインドして onWalletCardsRequested を呼び出します。このメソッドは、提供可能なカードの数とサイズに関するデータと、コールバックを含むリクエスト オブジェクトを受け取ります。コールバックはバックグラウンド スレッドから呼び出すことができます。

  4. QuickAccessWalletService は表示するカードを計算し、渡されたコールバックで onSuccess メソッドを呼び出します。サービスはこれらのアクションをバックグラウンド スレッドで実行することが推奨されます。

  5. カードが表示されるとすぐにシステム UI は onWalletCardSelected を呼び出し、最初のカードが選択されたことを QuickAccessWalletService に通知します。

    • onWalletCardSelected は、ユーザーが新しいカードを選択するたびに呼び出されます。
    • 現在選択されているカードが変更されなくても、onWalletCardSelected が呼び出されることがあります。
  6. ユーザーがクイック アクセス ウォレットを閉じると、システム UI が onWalletDismissed を呼び出して QuickAccessWalletService に通知します。

上記の例では、ユーザーはウォレットが表示されている間に、スマートフォンを NFC 支払いデバイスにかざします。NFC 支払いを処理するための重要なコンポーネントは HostApduService です。これは、NFC リーダーによって提供される APDU を処理するために実装する必要があります(詳しくは、ホストベースのカード エミュレーションをご覧ください)。ここでは、支払いアプリによって NFC デバイスとの通信の進行状況と結果を表示するアクティビティが開始されると想定しています。しかし、クイック アクセス ウォレット UI はアプリ ウィンドウの上に表示されます。つまり、支払いアクティビティがクイック アクセス ウォレット UI によって隠されてしまいます。これを修正するため、アプリはクイック アクセス ウォレット UI を閉じる必要があることをシステム UI に通知しなければなりません。そのためには、バインドされた QuickAccessWalletService への参照を取得し、イベントタイプ TYPE_NFC_PAYMENT_STARTED を使用して sendWalletServiceEvent を呼び出します。

QuickAccessWalletService 実装のサンプル

/** Sample implementation of {@link QuickAccessWalletService} */
@RequiresApi(VERSION_CODES.R)
public class MyQuickAccessWalletService extends QuickAccessWalletService {

  private static final String TAG = "QAWalletSvc";
  private ExecutorService executor;
  private PaymentCardManager paymentCardManager;

  @Override
  public void onCreate() {
    super.onCreate();
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
      Log.w(TAG, "Should not run on pre-R devices");
      stopSelf();
      return;
    }
    executor = Executors.newSingleThreadExecutor();
    paymentCardManager = new PaymentCardManager();
  }

  @Override
  public void onDestroy() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
      return;
    }
    executor.shutdownNow();
  }

  @Override
  public void onWalletCardsRequested(
      @NonNull GetWalletCardsRequest request, @NonNull GetWalletCardsCallback callback) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
      return;
    }
    executor.submit(
        () -> {
          List<PaymentCard> paymentCards = paymentCardManager.getCards();
          int maxCards = Math.min(paymentCards.size(), request.getMaxCards());
          List<WalletCard> walletCards = new ArrayList<>(maxCards);
          int selectedIndex = 0;
          int cardWidthPx = request.getCardWidthPx();
          int cardHeightPx = request.getCardHeightPx();
          for (int index = 0; index < maxCards; index++) {
            PaymentCard paymentCard = paymentCards.get(index);
            WalletCard walletCard =
                new WalletCard.Builder(
                        paymentCard.getCardId(),
                        paymentCard.getCardImage(cardWidthPx, cardHeightPx),
                        paymentCard.getContentDescription(),
                        paymentCard.getPendingIntent())
                    .build();
            walletCards.add(walletCard);
            if (paymentCard.isSelected()) {
              selectedIndex = index;
            }
          }
          GetWalletCardsResponse response =
              new GetWalletCardsResponse(walletCards, selectedIndex);
          callback.onSuccess(response);
        });
  }

  @Override
  public void onWalletCardSelected(@NonNull SelectWalletCardRequest request) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
      return;
    }
    executor.submit(
        () -> paymentCardManager.selectCardById(request.getCardId()));
  }

  @Override
  public void onWalletDismissed() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
      return;
    }
    executor.submit(() -> {
      paymentCardManager.removeCardOverrides();
    });
  }
}

QuickAccessWalletService の詳細については、QuickAccessWalletService API リファレンスをご覧ください。

権限

QuickAccessWalletService のマニフェスト エントリには、Android 11 で導入された android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE 権限が必要です。これはシステム UI が保持する署名レベルの権限です。つまり、システム UI プロセスのみが QuickAccessWalletService の実装にバインドできることを意味します。サイドロードされたアプリは、この権限を要求することにより、Android 10 以下を実行しているデバイスで QuickAccessWalletService データへの完全なアクセス権を取得できます。これを防ぐには、サービスが onCreate でビルド バージョンをチェックし、Android 11 以上を実行しているデバイスでのみサービスを有効にすることが推奨されます。ホストカード エミュレーション支払いサービスを提供するために必要な権限以外のアプリ権限は必要ありません。

デフォルトの NFC 支払いアプリが QuickAccessWalletService を実装していない場合またはエクスポートしない場合、クイック アクセス ウォレット UI は表示されません。

設定

ユーザーは、設定アプリからクイック アクセス ウォレット機能をオフにできます。設定ページは、[設定] > [システム] > [ジェスチャー] > [カードとパス] にあります。

クイック アクセス ウォレット機能を有効または無効にする設定ページ
図 3. クイック アクセス ウォレット機能を有効または無効にする設定ページ

カスタマイズ

クイック アクセス ウォレットのビューをシステム UI の別の場所に追加する

クイック アクセス ウォレット UI は、システム プラグインとして構築されています。AOSP 実装では GlobalActionsDialog(長押しで表示されます)でこの UI を使用しますが、プラグイン インターフェースで指定されたコントラクトを維持する限り、この機能を別のジェスチャーに移動できます。

public interface GlobalActionsPanelPlugin extends Plugin {

  /** Invoked when the view is shown */
  PanelViewController onPanelShown(Callbacks callbacks, boolean deviceLocked);

  /** Callbacks for interacting with the view container */
  interface Callbacks {
    /** Dismisses the view */
    void dismissGlobalActionsMenu();

    /** Starts a PendingIntent, dismissing the keyguard if necessary. */
    void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent);
  }

  /** Provides the Quick Access Wallet view */
  interface PanelViewController {

    /** Returns the QuickAccessWallet view, which may take any size */
    View getPanelContent();

    /** Invoked when the view is dismissed */
    void onDismissed();

    /** Invoked when the device is either locked or unlocked. */
    void onDeviceLockStateChanged(boolean locked);
  }
}

クイック アクセス ウォレット UI は、GlobalActionsPanelPluginPanelViewController を実装します。GlobalActionsDialog は、com.android.systemui.Dependency を使用してウォレット プラグインのインスタンスを取得します。

GlobalActionsPanelPlugin mPanelPlugin =
    Dependency.get(ExtensionController.class)
        .newExtension(GlobalActionsPanelPlugin.class)
        .withPlugin(GlobalActionsPanelPlugin.class)
        .build()
        .get();

プラグインが null でないことと、onPanelShown によって返された PanelViewController が null ではないことを確認した後、ダイアログはgetPanelContent によって提供された View を自身の View にアタッチし、システム イベント用の適切なコールバックを提供します。

// Construct a Wallet PanelViewController.
// `this` implements GlobalActionsPanelPlugin.Callbacks
GlobalActionsPanelPlugin.PanelViewController mPanelController =
    mPanelPlugin.onPanelShown(this, !mKeyguardStateController.isUnlocked());

// Attach the view
FrameLayout panelContainer = findViewById(R.id.my_panel_container);
FrameLayout.LayoutParams panelParams =
    new FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT);
panelContainer.addView(mPanelController.getPanelContent(), panelParams);

// Respond to unlock events (if the view can be accessed while the phone is locked)
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
  @Override
  public void onUnlockedChanged() {
    boolean unlocked = keyguardStateController.isUnlocked()
        || keyguardStateController.canDismissLockScreen();
    mPanelController.onDeviceLockStateChanged(unlocked);
  }
});

// Implement GlobalActionsPanelPlugin.Callbacks
@Override
public void dismissGlobalActionsMenu() {
  dismissDialog();
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent) {
  mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
}

// Notify the wallet when the container view is dismissed
mPanelController.onDismissed();

電源ボタンメニューからクイック アクセス ウォレットを削除するには、システムビルドから QuickAccessWallet ターゲットを取り除きます。電源ボタンメニューからクイック アクセス ウォレットを削除してシステム UI が提供する別のビューに追加するには、ビルド ターゲットを組み込んで、GlobalActionsPanelPlugin への参照を GlobalActionsImpl から削除します。

デフォルト構成の設定

クイック アクセス ウォレットの可視性は、GLOBAL_ACTIONS_PANEL_ENABLEDGLOBAL_ACTIONS_PANEL_AVAILABLE という 2 つの安全な設定によって制御されます。AVAILABLE 設定は、この機能を設定ページでオンまたはオフにできるかどうかを制御します。この設定は、WalletPluginService によって true に設定されています。QuickAccessWallet がビルドに含まれない場合、設定は false のままです。同じ場所で、ENABLED 設定はデフォルトで true に設定されていますが、ユーザーは設定ページでオフにできます。デフォルトの動作を変更するには、WalletPluginService#enableFeatureInSettings を変更します。

検証

クイック アクセス ウォレットの実装を検証するには、CTS テストと手動テストを実施します。プラグインを変更した場合は、付属の robolectric テストも実施する必要があります。

CTS テスト

cts/tests/quickaccesswallet にある CTS テストを実施します。

手動テスト

クイック アクセス ウォレットのコア機能をテストするには、NFC 支払いデバイス(本物または偽物)と、QuickAccessWalletService を実装した NFC 支払いアプリ(ウォレット アプリ)が必要です。テストが必要なコア機能には、可用性、ゼロ状態、カード選択、ロック画面の動作などがあります。

可用性

  • GLOBAL_ACTIONS_PANEL_ENABLED 設定が true で、デフォルトの NFC 支払いアプリがこの機能をサポートしている場合、クイック アクセス ウォレットは利用可能です。
  • GLOBAL_ACTIONS_PANEL_ENABLED 設定が false で、デフォルトの NFC 支払いアプリがこの機能をサポートしている場合、クイック アクセス ウォレットは利用不可です。
  • GLOBAL_ACTIONS_PANEL_ENABLED 設定が true で、デフォルトの NFC 支払いアプリがこの機能をサポートしていない場合、クイック アクセス ウォレットは利用不可です。
  • GLOBAL_ACTIONS_PANEL_ENABLED 設定が false で、デフォルトの NFC 支払いアプリがこの機能をサポートしていない場合、クイック アクセス ウォレットは利用不可です。

ゼロ状態

  • QuickAccessWalletService が有効になっていてエクスポートされているがカードを提供していない場合、クイック アクセス ウォレット UI は空の状態のビューを表示します。
  • 空の状態のビューをクリックすると、ウォレット アプリが開きます。

    クイック アクセス ウォレット UI の空の状態のビュー
    図 4. クイック アクセス ウォレット UI の空の状態のビュー

ゼロでない状態

  • ウォレット アプリが 1 つ以上のカードを提供している場合、クイック アクセス ウォレット UI にカードが表示されます。

    カードが表示されたクイック アクセス ウォレット UI
    図 5. カードが表示されたクイック アクセス ウォレット UI
  • 表示されたカードが NFC 支払い方法を表している場合、スマートフォンを NFC 支払いデバイスにかざすと、その支払い方法が使用され、ウォレット ビューが閉じます。

  • 表示されたカードをクリックすると、ウォレット ビューが閉じて、そのカードの詳細アクティビティが開きます。

  • QuickAccessWalletService によって複数のカードが提供されている場合、ユーザーはカード間をスワイプできます。

  • オーバーフロー メニューには 2 つのエントリが表示されます。1 つはウォレット アプリを開くエントリで、もう 1 つは設定ページの [カードとパスの表示] 画面を開くエントリです。

ロック状態のテスト

  • スマートフォンがロックされている場合、ウォレットの可視性は Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT 設定によって制御されます。これは設定ページで管理できます。
  • スマートフォンがロックされていて POWER_MENU_LOCK_SHOW_CONTENTfalse の場合、ウォレットは表示されません。
  • スマートフォンがロックされていて、POWER_MENU_LOCK_SHOW_CONTENTtrue の場合、ウォレットは表示されます。
  • ウォレットがロック画面に表示されているときにスマートフォンのロックを解除すると、カードが再度クエリされ、異なるカード コンテンツが作成されることがあります。

ユーザー補助機能のテスト

  • TalkBack のユーザーは、左右のスワイプを行い、カードの内容説明を聞くことにより、ウォレット ビューを操作できます。
  • TalkBack を有効にして左右にスワイプすると、各カードが順番に選択されます。TalkBack のユーザーは、NFC 支払いデバイスで NFC 支払い方法を選択して使用できます。