The Quick Access Wallet feature, available from Android 11, allows the user to access payment cards and relevant passes directly from the power menu. Major use cases include selecting the appropriate payment method before performing a transaction at an NFC terminal and quickly accessing flights and other passes for upcoming events.
In Android 12 or higher, the Quick Access Wallet feature is available from the shade as shown in Figure 1 and Figure 2.
In Android 11, the feature is available from the power menu as shown in Figure 3.
Requirements
Your device must have NFC to use the Quick Access Wallet feature. The feature
binds to QuickAccessWalletService
of the default NFC payment app, which means
that the device must also support NFC
host-based card emulation (HCE).
Feature overview
There are two parts to the Quick Access Wallet: the Quick Access Wallet UI and the Quick Access Wallet card provider.
In Android 12 or higher, the Wallet UI runs in System
UI and is located in
frameworks/base/packages/SystemUI/src/com/android/systemui/wallet
. In
Android 11, the Wallet UI, which is located in
platform/packages/apps/QuickAccessWallet
,
must be installed and whitelisted.
The Quick Access Wallet card provider is the default NFC payment app. Users can
have multiple NFC payment apps installed simultaneously, but only the default
NFC payment app can show cards on the power menu. You can specify which NFC
payment app is set as the default initially, but users can select a different
app in Settings. If only one NFC payment app is installed, it becomes the
default automatically (see
CardEmulationManager
).
Implementation
To provide cards to the Quick Access Wallet UI, NFC payment
apps must implement
QuickAccessWalletService
.
Payment apps must include a manifest entry advertising the service.
To ensure that only the System UI can bind to QuickAccessWalletService
, the
NFC payment app must require the
android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE
permission. Requiring this
permission ensures that
only the System UI can bind to 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" />
<meta-data
android:name="android.quickaccesswallet.tile"
android:resource="@drawable/my_default_tile_icon"/>
</service>
Additional information about the wallet is included in the linked XML file:
<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"/>
Next, the payment app must implement 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
}
}
If HostApduService
starts to handle an NFC transaction and, as a consequence,
starts an activity to display the progress and outcome of the payment, it should
also try to get a reference to the bound QuickAccessWalletService
and call
QuickAccessWalletService#sendEvent
with an event type of
TYPE_NFC_PAYMENT_STARTED
. This causes the Quick Access Wallet UI to be
dismissed, thus allowing the user an unobstructed view of the payment activity.
For additional documentation on implementing QuickAccessWalletService
, see
QuickAccessWalletService
and the
TestQuickAccessWalletService
CTS test.
Enable Quick Access Wallet UI in Android 11
To configure the Quick Access Wallet to be available from the power menu
in Android 11, include the
QuickAccessWallet
target in the build and enable the globalactions.wallet
plugin by adding
the line in bold in the code sample below to the
overlay/frameworks/base/packages/SystemUI/res/values/config.xml
file.
<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>
Specify the default NFC payment app in the
settings configuration file
using def_nfc_payment_component
.
The default NFC payment app must expose
QuickAccessWalletService
to provide cards to the Quick Access Wallet. If the default NFC payment app
doesn't export this service, the wallet UI is hidden.
QuickAccessWalletService implementation details
QuickAccessWalletService
has three abstract methods that must be implemented:
onWalletCardsRequested
, onWalletCardSelected
, and onWalletDismissed
. The
sequence diagram below illustrates a call sequence when the Quick Access Wallet
is viewed immediately preceding an NFC payment.
Not all views of the Quick Access Wallet are followed by an NFC payment, but
Figure 4 above illustrates all of the capabilities of
QuickAccessWalletService
. In this example, the Quick Access Wallet card
provider implements the elements outlined in blue. It's assumed that payment
cards are stored on the device in a database and are accessed through an
interface named PaymentCardManager
. It's further assumed that an activity
called PaymentActivity
displays the result of an NFC payment. The flow
proceeds as follows:
- The user performs a gesture to bring up the Quick Access Wallet.
The Quick Access Wallet UI (part of System UI) checks the package manager to see if the default NFC payment app exports
QuickAccessWalletService
.- If the service isn't exported, the Quick Access Wallet isn't displayed.
The Quick Access Wallet UI binds to the
QuickAccessWalletService
and callsonWalletCardsRequested
. This method takes a request object containing data about the number and size of the cards that can be provided and a callback. The callback can be called from a background thread.QuickAccessWalletService
computes the cards that it wants to show, then calls theonSuccess
method on the provided callback. It's recommended that the service perform these actions on a background thread.As soon as the cards are displayed, the System UI notifies
QuickAccessWalletService
that the first card has been selected by callingonWalletCardSelected
.onWalletCardSelected
is called every time the user selects a new card.onWalletCardSelected
might be called even if the currently selected card hasn't changed.
When the user dismisses the Quick Access Wallet, the System UI notifies
QuickAccessWalletService
by callingonWalletDismissed
.
In the above example, the user brings the phone into range of an NFC payment
terminal while the wallet is being displayed. A key component of handling NFC
payments is HostApduService
, which must be implemented to process APDUs
provided by the NFC reader (for more information, see
Host-based card emulation).
It's assumed that the payment app starts an activity to display the progress and
result of the interaction with the NFC terminal. However, the Quick Access
Wallet UI is displayed on top of the app window, meaning that the
payment activity is obscured by the Quick Access Wallet UI. To rectify this, the
app must notify the System UI that the Quick Access Wallet UI should be
dismissed. It can do so by getting a reference to the bound
QuickAccessWalletService
and calling sendWalletServiceEvent
with the event
type TYPE_NFC_PAYMENT_STARTED
.
QuickAccessWalletService sample implementation
/** 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();
});
}
}
For further details about QuickAccessWalletService
, see
QuickAccessWalletService
API reference.
Permissions
The manifest entry for QuickAccessWalletService
must require the
android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE
permission introduced in
Android 11. This is a signature-level permission held
by the System UI, which means that
only the System UI process can bind to implementations of
QuickAccessWalletService
. Be aware that side-loaded apps can claim this
permission and get full access to QuickAccessWalletService
data on devices
running Android 10 or lower. To prevent this, it's recommended that the service
check the build version in onCreate
and enable the service only on devices
running Android 11 and higher. No other app permissions
are required beyond those necessary to provide host card emulation payment
services.
If the default NFC payment app doesn't implement or export
QuickAccessWalletService
, the Quick Access Wallet UI isn't displayed.
Settings in Android 12
To enable or disable the Quick Access Wallet from the lock screen, users can use the Show wallet toggle in Settings > Display > Lock screen. To disable the wallet in the shade, users must manually edit it in the quick settings shade.
Figure 5. Show wallet toggle in the Lock screen page in Settings.
Settings in Android 11
Users can turn off the Quick Access Wallet feature from the Settings app. The settings page is found in Settings > System > Gestures > Cards & passes.
Customization
Add Quick Access Wallet view to another location in System UI
The
Quick Access Wallet UI
is built as a
system plugin.
Although the AOSP implementation makes use of it in
GlobalActionsDialog
(shown on long power press), you can move the feature behind a different gesture
as long as you maintain the contract specified by the plugin interface.
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);
}
}
The
Quick Access Wallet UI
implements GlobalActionsPanelPlugin
and PanelViewController
.
GlobalActionsDialog
gets an instance of the wallet plugin by using
com.android.systemui.Dependency
:
GlobalActionsPanelPlugin mPanelPlugin =
Dependency.get(ExtensionController.class)
.newExtension(GlobalActionsPanelPlugin.class)
.withPlugin(GlobalActionsPanelPlugin.class)
.build()
.get();
After checking that the plugin is non-null and that the PanelViewController
returned by onPanelShown
is non-null, the dialog attaches the View
provided
by getPanelContent
to its own View
and provides appropriate callbacks for
system events.
// 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();
To remove the Quick Access Wallet from the power menu, omit the
QuickAccessWallet
target from the system build. To remove the Quick Access
Wallet from the power menu but add it to a different System UI provided view,
include the build target and remove references to the GlobalActionsPanelPlugin
from
GlobalActionsImpl
.
Set default configurations
Android 12
In Android 12 or higher, the Quick Access Wallet is
always visible in the quick settings shade. Visibility of the Quick Access
Wallet in the lock screen is gated by the following secure setting:
LOCKSCREEN_SHOW_WALLET
. This setting controls whether the Quick Access Wallet
icon is shown on the bottom right of the lock screen. This setting is set to
true
by default, but can be turned off by the user in Settings >
Display > Lock screen > Show wallet.
Android 11
In Android 11, Quick Access Wallet visibility is gated by two secure settings:
GLOBAL_ACTIONS_PANEL_ENABLED
and GLOBAL_ACTIONS_PANEL_AVAILABLE
. The
AVAILABLE
setting controls whether the feature can be turned on and off in
Settings. This setting is set to true
by
WalletPluginService
.
If QuickAccessWallet
isn't included in the build, then the setting remains
false
. The ENABLED
setting is set to true
by default in the same place,
but can be turned off by the user in Settings. To change the default behavior,
modify WalletPluginService#enableFeatureInSettings
.
Validation
To validate your implementation of the Quick Access Wallet, run CTS and manual tests. Changes to the plugin should also exercise the included robolectric tests.
CTS tests
Run the CTS tests located at
cts/tests/quickaccesswallet
.
Manual tests for Android 12
Testing the core features of the Quick Access Wallet requires an NFC payment
terminal (real or fake) and an NFC payment app that implements
QuickAccessWalletService
(wallet app). Core features that must be tested
include: availability, zero state, card selection, and lock screen behavior.
Availability
- If the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible in neither the quick settings nor the lock screen.
- If the default NFC payment app supports the feature, the Quick Access Wallet is accessible in the quick settings shade.
- If the default NFC payment app supports the feature and if the
LOCKSCREEN_SHOW_WALLET
setting istrue
, the Quick Access Wallet is accessible on the lock screen. - If the default NFC payment app supports the feature and if the
LOCKSCREEN_SHOW_WALLET
setting isfalse
, the Quick Access Wallet isn't accessible on the lock screen.
Zero state
If
QuickAccessWalletService
is enabled and exported but doesn't provide any cards, the tile in the Shade appears as shown in the example in Figure 7. Clicking on the tile opens the default NFC payment app.Figure 7. Example tile in the shade showing default NFC payment app.
Clicking on the empty state view as shown in Figure 8 opens the default NFC payment app. This empty state view is displayed only when the user has one card left in the wallet, removes the card from the card detail page, and then goes back to the wallet view.
The lock screen shows the wallet icon.
Figure 8. Empty state view in the Quick Access Wallet UI.
Non-zero state
If the wallet app provides one or more cards, the tile in the shade appears as shown in Figure 9.
Figure 9. Example tile in the shade when wallet app has one or more cards.
Clicking on the tile shows a card carousel.
The lock screen shows a button that opens the Quick Access Wallet.
Figure 10. Quick Access Wallet UI with a card displayed.
If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.
Clicking on a displayed card opens the detailed activity for that card.
If multiple cards are provided by
QuickAccessWalletService
, the user is able to swipe between cards.The overflow menu contains one entry: open the lock screen settings so that the user can change the Show wallet option.
Lock state tests
- If the phone is locked, the wallet is visible on the quick settings shade, with a description of Add a card if no card exists in the default payment app, or unlock to use if cards exist in the default payment app.
- If the phone is locked, wallet visibility on the lock screen is
controlled by the
Secure.LOCKSCREEN_SHOW_WALLET
setting, which is controlled in Settings. - If the phone is locked,
LOCKSCREEN_SHOW_WALLET
isfalse
, and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen. - If the phone is locked,
LOCKSCREEN_SHOW_WALLET
istrue
, and no card exists in the default NFC payment app, the wallet isn't displayed on the lock screen. - If the phone is locked,
LOCKSCREEN_SHOW_WALLET
istrue
, and cards exist in the default NFC payment app, the wallet is displayed on the lock screen. - Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being requeried, which might result in different card content.
Accessibility tests
- Talkback users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
- Swiping left and right with Talkback enabled selects each card in turn. Talkback users can select and use an NFC payment method at an NFC payment terminal.
Manual tests for Android 11
Testing the core features of the Quick Access Wallet requires an NFC payment
terminal (real or fake) and an NFC payment app that implements
QuickAccessWalletService
(wallet app). Core features that must be tested
include availability, zero state, card selection, and lock screen behavior.
Availability
- If the
GLOBAL_ACTIONS_PANEL_ENABLED
setting istrue
and the default NFC payment app supports the feature, the Quick Access Wallet is accessible. - If the
GLOBAL_ACTIONS_PANEL_ENABLED
setting isfalse
and the default NFC payment app supports the feature, the Quick Access Wallet isn't accessible. - If the
GLOBAL_ACTIONS_PANEL_ENABLED
setting istrue
and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible. - If the
GLOBAL_ACTIONS_PANEL_ENABLED
setting isfalse
and the default NFC payment app doesn't support the feature, the Quick Access Wallet isn't accessible.
Zero state
- If
QuickAccessWalletService
is enabled and exported but doesn't provide any cards, the Quick Access Wallet UI displays the empty state view. Clicking the empty state view opens the wallet app.
Non-zero state
If the wallet app provides one or more cards, the cards are displayed in the Quick Access Wallet UI.
If the displayed card represents an NFC payment method, holding the phone to an NFC payment terminal results in that payment method being used and the wallet view is dismissed.
Clicking a displayed card dismisses the wallet view and opens the detailed activity for that card.
If multiple cards are provided by
QuickAccessWalletService
, the user is able to swipe between cards.The overflow menu contains two entries: one that opens the wallet app and one that opens the Show cards & passes screen in Settings.
Lock state tests
- If the phone is locked, wallet visibility is controlled by the
Settings.Secure.POWER_MENU_LOCK_SHOW_CONTENT
setting, which can be controlled in Settings. - If the phone is locked and
POWER_MENU_LOCK_SHOW_CONTENT
isfalse
, the wallet isn't displayed. - If the phone is locked and
POWER_MENU_LOCK_SHOW_CONTENT
istrue
, the wallet is displayed. - Unlocking the phone while the wallet is being displayed on the lock screen results in the cards being re-queried, which might result in different card content.
Accessibility tests
- TalkBack users can navigate the wallet view by swiping left and right and by listening to the content descriptions of the cards.
- Swiping left and right with TalkBack enabled selects each card in turn. TalkBack users can select and use an NFC payment method at an NFC payment terminal.