Add QuickAccessWallet API

Adds a new API that allows applications to provide payment cards and
other relevant passes to SysUI which are then shown in the Quick Access
Wallet (long press on Pixel). See go/aospqaw-dd for details.

Bug: 144342153
Test: manual - started device, didn't blow up
Test: atest - run from frameworks/base dir
Change-Id: I8fef3116e6e4bd1f8a4f5a907892ea8993b49b0e
diff --git a/api/current.txt b/api/current.txt
index d8fac42..c1bb19e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -39,6 +39,7 @@
     field public static final String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
+    field public static final String BIND_QUICK_ACCESS_WALLET_SERVICE = "android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE";
     field public static final String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
     field public static final String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
     field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
@@ -39903,6 +39904,7 @@
     field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
     field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
     field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = "android.settings.PROCESS_WIFI_EASY_CONNECT_URI";
+    field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS";
     field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
     field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
@@ -42946,6 +42948,94 @@
 
 }
 
+package android.service.quickaccesswallet {
+
+  public final class GetWalletCardsCallback {
+    method public void onFailure(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
+    method public void onSuccess(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
+  }
+
+  public final class GetWalletCardsError implements android.os.Parcelable {
+    ctor public GetWalletCardsError(@Nullable android.graphics.drawable.Icon, @Nullable CharSequence);
+    method public int describeContents();
+    method @Nullable public android.graphics.drawable.Icon getIcon();
+    method @Nullable public CharSequence getMessage();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.GetWalletCardsError> CREATOR;
+  }
+
+  public final class GetWalletCardsRequest implements android.os.Parcelable {
+    ctor public GetWalletCardsRequest(int, int, int, int);
+    method public int describeContents();
+    method public int getCardHeightPx();
+    method public int getCardWidthPx();
+    method public int getIconSizePx();
+    method public int getMaxCards();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.GetWalletCardsRequest> CREATOR;
+  }
+
+  public final class GetWalletCardsResponse implements android.os.Parcelable {
+    ctor public GetWalletCardsResponse(@NonNull java.util.List<android.service.quickaccesswallet.WalletCard>, int);
+    method public int describeContents();
+    method public int getSelectedIndex();
+    method @NonNull public java.util.List<android.service.quickaccesswallet.WalletCard> getWalletCards();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.GetWalletCardsResponse> CREATOR;
+  }
+
+  public abstract class QuickAccessWalletService extends android.app.Service {
+    ctor public QuickAccessWalletService();
+    method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onWalletCardSelected(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
+    method public abstract void onWalletCardsRequested(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.GetWalletCardsCallback);
+    method public abstract void onWalletDismissed();
+    method public final void sendWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
+    field public static final String ACTION_DISMISS_WALLET = "android.service.quickaccesswallet.action.DISMISS_WALLET";
+    field public static final String ACTION_VIEW_WALLET = "android.service.quickaccesswallet.action.VIEW_WALLET";
+    field public static final String ACTION_VIEW_WALLET_SETTINGS = "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
+    field public static final String SERVICE_INTERFACE = "android.service.quickaccesswallet.QuickAccessWalletService";
+    field public static final String SERVICE_META_DATA = "android.quickaccesswallet";
+  }
+
+  public final class SelectWalletCardRequest implements android.os.Parcelable {
+    ctor public SelectWalletCardRequest(@NonNull String);
+    method public int describeContents();
+    method @NonNull public String getCardId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.SelectWalletCardRequest> CREATOR;
+  }
+
+  public final class WalletCard implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.graphics.drawable.Icon getCardIcon();
+    method @NonNull public String getCardId();
+    method @NonNull public android.graphics.drawable.Icon getCardImage();
+    method @Nullable public CharSequence getCardLabel();
+    method @NonNull public CharSequence getContentDescription();
+    method @NonNull public android.app.PendingIntent getPendingIntent();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.WalletCard> CREATOR;
+  }
+
+  public static final class WalletCard.Builder {
+    ctor public WalletCard.Builder(@NonNull String, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence, @NonNull android.app.PendingIntent);
+    method @NonNull public android.service.quickaccesswallet.WalletCard build();
+    method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardLabel(@Nullable CharSequence);
+  }
+
+  public final class WalletServiceEvent implements android.os.Parcelable {
+    ctor public WalletServiceEvent(int);
+    method public int describeContents();
+    method public int getEventType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.WalletServiceEvent> CREATOR;
+    field public static final int TYPE_NFC_PAYMENT_STARTED = 1; // 0x1
+  }
+
+}
+
 package android.service.quicksettings {
 
   public final class Tile implements android.os.Parcelable {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b97482a..7ebe7f1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1969,6 +1969,21 @@
             "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
 
     /**
+     * Activity Action: Show screen for controlling the Quick Access Wallet.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: The Intent's data URI specifies the application package name
+     * to be shown, with the "package" scheme.  That is "package:com.my.app".
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS =
+            "android.settings.QUICK_ACCESS_WALLET_SETTINGS";
+
+    /**
      * Activity Action: Show screen for controlling which apps have access on volume directories.
      * <p>
      * Input: Nothing.
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
new file mode 100644
index 0000000..9d210cd
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
+ */
+public final class GetWalletCardsCallback {
+
+    private static final String TAG = "QAWalletCallback";
+
+    private final IQuickAccessWalletServiceCallbacks mCallback;
+    private final Handler mHandler;
+    private boolean mCalled;
+
+    /**
+     * @hide
+     */
+    GetWalletCardsCallback(IQuickAccessWalletServiceCallbacks callback, Handler handler) {
+        mCallback = callback;
+        mHandler = handler;
+    }
+
+    /**
+     * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+     * was successfully handled by the service.
+     *
+     * @param response The response contains the list of {@link WalletCard walletCards} to be shown
+     *                 to the user as well as the index of the card that should initially be
+     *                 presented as the selected card.
+     */
+    public void onSuccess(@NonNull GetWalletCardsResponse response) {
+        mHandler.post(() -> onSuccessInternal(response));
+    }
+
+    /**
+     * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+     * could not be handled by the service.
+     *
+     * @param error The error message. <b>Note: </b> this message should <b>not</b> contain PII
+     *              (Personally Identifiable Information, such as username or email address).
+     * @throws IllegalStateException if this method or {@link #onSuccess} was already called.
+     */
+    public void onFailure(@NonNull GetWalletCardsError error) {
+        mHandler.post(() -> onFailureInternal(error));
+    }
+
+    private void onSuccessInternal(GetWalletCardsResponse response) {
+        if (mCalled) {
+            Log.w(TAG, "already called");
+            return;
+        }
+        mCalled = true;
+        try {
+            mCallback.onGetWalletCardsSuccess(response);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error returning wallet cards", e);
+        }
+    }
+
+    private void onFailureInternal(GetWalletCardsError error) {
+        if (mCalled) {
+            Log.w(TAG, "already called");
+            return;
+        }
+        mCalled = true;
+        try {
+            mCallback.onGetWalletCardsFailure(error);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error returning failure message", e);
+        }
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsError.aidl b/core/java/android/service/quickaccesswallet/GetWalletCardsError.aidl
new file mode 100644
index 0000000..847f5ac
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsError.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable GetWalletCardsError;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsError.java b/core/java/android/service/quickaccesswallet/GetWalletCardsError.java
new file mode 100644
index 0000000..527d2b7
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsError.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Error response for an {@link GetWalletCardsRequest}.
+ */
+public final class GetWalletCardsError implements Parcelable {
+
+    private final Icon mIcon;
+    private final CharSequence mMessage;
+
+    /**
+     * Construct a new error response. If provided, the icon and message will be displayed to the
+     * user.
+     *
+     * @param icon    an icon to be shown to the user next to the message. Optional.
+     * @param message message to be shown to the user. Optional.
+     */
+    public GetWalletCardsError(@Nullable Icon icon, @Nullable CharSequence message) {
+        mIcon = icon;
+        mMessage = message;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        if (mIcon == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            mIcon.writeToParcel(dest, flags);
+        }
+        TextUtils.writeToParcel(mMessage, dest, flags);
+    }
+
+    private static GetWalletCardsError readFromParcel(Parcel source) {
+        Icon icon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source);
+        CharSequence message = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        return new GetWalletCardsError(icon, message);
+    }
+
+    @NonNull
+    public static final Creator<GetWalletCardsError> CREATOR =
+            new Creator<GetWalletCardsError>() {
+                @Override
+                public GetWalletCardsError createFromParcel(Parcel source) {
+                    return readFromParcel(source);
+                }
+
+                @Override
+                public GetWalletCardsError[] newArray(int size) {
+                    return new GetWalletCardsError[size];
+                }
+            };
+
+    /**
+     * An icon that may be displayed with the message to provide a visual indication of why cards
+     * could not be provided in the Quick Access Wallet.
+     */
+    @Nullable
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * A localized message that may be shown to the user in the event that the wallet cards cannot
+     * be retrieved. <b>Note: </b> this message should <b>not</b> contain PII (Personally
+     * Identifiable Information, such as username or email address).
+     */
+    @Nullable
+    public CharSequence getMessage() {
+        return mMessage;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.aidl b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.aidl
new file mode 100644
index 0000000..e70a982
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable GetWalletCardsRequest;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.java b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.java
new file mode 100644
index 0000000..2ba448f
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a request to a {@link QuickAccessWalletService} for {@link WalletCard walletCards}.
+ * Wallet cards may represent anything that a user might carry in their wallet -- a credit card,
+ * library card, a transit pass, etc. This request contains the desired size of the card images and
+ * icons as well as the maximum number of cards that may be returned in the {@link
+ * GetWalletCardsResponse}.
+ *
+ * <p>Cards may be displayed with an optional icon and label. The icon and label should communicate
+ * the same idea. For example, if a card can be used at an NFC terminal, the icon could be an NFC
+ * icon and the label could inform the user how to interact with the NFC terminal.
+ *
+ * <p>The maximum number of cards that may be displayed in the wallet is provided in {@link
+ * #getMaxCards()}. The {@link QuickAccessWalletService} may provide up to this many cards in the
+ * {@link GetWalletCardsResponse#getWalletCards()}. If the list of cards provided exceeds this
+ * number, some of the cards may not be shown to the user.
+ */
+public final class GetWalletCardsRequest implements Parcelable {
+
+    private final int mCardWidthPx;
+    private final int mCardHeightPx;
+    private final int mIconSizePx;
+    private final int mMaxCards;
+
+    /**
+     * Creates a new GetWalletCardsRequest.
+     *
+     * @param cardWidthPx  The width of the card image in pixels.
+     * @param cardHeightPx The height of the card image in pixels.
+     * @param iconSizePx   The width and height of the optional card icon in pixels.
+     * @param maxCards     The maximum number of cards that may be provided in the response.
+     */
+    public GetWalletCardsRequest(int cardWidthPx, int cardHeightPx, int iconSizePx, int maxCards) {
+        this.mCardWidthPx = cardWidthPx;
+        this.mCardHeightPx = cardHeightPx;
+        this.mIconSizePx = iconSizePx;
+        this.mMaxCards = maxCards;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mCardWidthPx);
+        dest.writeInt(mCardHeightPx);
+        dest.writeInt(mIconSizePx);
+        dest.writeInt(mMaxCards);
+    }
+
+    @NonNull
+    public static final Creator<GetWalletCardsRequest> CREATOR =
+            new Creator<GetWalletCardsRequest>() {
+                @Override
+                public GetWalletCardsRequest createFromParcel(Parcel source) {
+                    int cardWidthPx = source.readInt();
+                    int cardHeightPx = source.readInt();
+                    int iconSizePx = source.readInt();
+                    int maxCards = source.readInt();
+                    return new GetWalletCardsRequest(cardWidthPx,
+                            cardHeightPx,
+                            iconSizePx,
+                            maxCards);
+                }
+
+                @Override
+                public GetWalletCardsRequest[] newArray(int size) {
+                    return new GetWalletCardsRequest[size];
+                }
+            };
+
+    /**
+     * The desired width of the {@link WalletCard#getCardImage()}, in pixels. The dimensions of the
+     * card image are requested so that it may be rendered without scaling.
+     * <p>
+     * The {@code cardWidthPx} and {@code cardHeightPx} should be applied to the size of the {@link
+     * WalletCard#getCardImage()}. The size of the card image is specified so that it may be
+     * rendered accurately and without distortion caused by scaling.
+     */
+    public int getCardWidthPx() {
+        return mCardWidthPx;
+    }
+
+    /**
+     * The desired height of the {@link WalletCard#getCardImage()}, in pixels. The dimensions of the
+     * card image are requested so that it may be rendered without scaling.
+     */
+    public int getCardHeightPx() {
+        return mCardHeightPx;
+    }
+
+    /**
+     * Wallet cards may be displayed next to an icon. The icon can help to convey additional
+     * information about the state of the card. If the provided icon is a bitmap, its width and
+     * height should equal iconSizePx so that it is rendered without distortion caused by scaling.
+     */
+    public int getIconSizePx() {
+        return mIconSizePx;
+    }
+
+    /**
+     * The maximum size of the {@link GetWalletCardsResponse#getWalletCards()}. If the list of cards
+     * exceeds this number, not all cards may be displayed.
+     */
+    public int getMaxCards() {
+        return mMaxCards;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.aidl b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.aidl
new file mode 100644
index 0000000..b0f25b3
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable GetWalletCardsResponse;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
new file mode 100644
index 0000000..996622a
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The response for an {@link GetWalletCardsRequest} contains a list of wallet cards and the index
+ * of the card that should initially be displayed in the 'selected' position.
+ */
+public final class GetWalletCardsResponse implements Parcelable {
+
+    private final List<WalletCard> mWalletCards;
+    private final int mSelectedIndex;
+
+    /**
+     * Construct a new response.
+     *
+     * @param walletCards   The list of wallet cards.
+     * @param selectedIndex The index of the card that should be presented as the initially
+     *                      'selected' card
+     */
+    public GetWalletCardsResponse(@NonNull List<WalletCard> walletCards, int selectedIndex) {
+        this.mWalletCards = walletCards;
+        this.mSelectedIndex = selectedIndex;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mWalletCards.size());
+        dest.writeParcelableList(mWalletCards, flags);
+        dest.writeInt(mSelectedIndex);
+    }
+
+    private static GetWalletCardsResponse readFromParcel(Parcel source) {
+        int size = source.readInt();
+        List<WalletCard> walletCards =
+                source.readParcelableList(new ArrayList<>(size), WalletCard.class.getClassLoader());
+        int selectedIndex = source.readInt();
+        return new GetWalletCardsResponse(walletCards, selectedIndex);
+    }
+
+    @NonNull
+    public static final Creator<GetWalletCardsResponse> CREATOR =
+            new Creator<GetWalletCardsResponse>() {
+                @Override
+                public GetWalletCardsResponse createFromParcel(Parcel source) {
+                    return readFromParcel(source);
+                }
+
+                @Override
+                public GetWalletCardsResponse[] newArray(int size) {
+                    return new GetWalletCardsResponse[size];
+                }
+            };
+
+    /**
+     * The list of {@link WalletCard}s. The size of this list should not exceed {@link
+     * GetWalletCardsRequest#getMaxCards()}.
+     */
+    @NonNull
+    public List<WalletCard> getWalletCards() {
+        return mWalletCards;
+    }
+
+    /**
+     * The {@code selectedIndex} represents the index of the card that should be presented in the
+     * 'selected' position when the cards are initially displayed in the quick access wallet.  The
+     * {@code selectedIndex} should be greater than or equal to zero and less than the size of the
+     * list of {@link WalletCard walletCards}, unless the list is empty in which case the {@code
+     * selectedIndex} can take any value. 0 is a nice round number for such cases.
+     */
+    public int getSelectedIndex() {
+        return mSelectedIndex;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl b/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl
new file mode 100644
index 0000000..ee70be4
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.service.quickaccesswallet.GetWalletCardsRequest;
+import android.service.quickaccesswallet.IQuickAccessWalletServiceCallbacks;
+import android.service.quickaccesswallet.SelectWalletCardRequest;
+import android.service.quickaccesswallet.WalletServiceEvent;
+import android.service.quickaccesswallet.WalletServiceEventListenerRequest;
+
+/**
+ * Implemented by QuickAccessWalletService in the payment application
+ *
+ * @hide
+ */
+interface IQuickAccessWalletService {
+    // Request to get cards, which should be provided using the callback.
+    oneway void onWalletCardsRequested(
+        in GetWalletCardsRequest request, in IQuickAccessWalletServiceCallbacks callback);
+    // Indicates that a card has been selected.
+    oneway void onWalletCardSelected(in SelectWalletCardRequest request);
+    // Sent when the wallet is dismissed or closed.
+    oneway void onWalletDismissed();
+    // Register an event listener
+    oneway void registerWalletServiceEventListener(
+        in WalletServiceEventListenerRequest request,
+        in IQuickAccessWalletServiceCallbacks callback);
+    // Unregister an event listener
+    oneway void unregisterWalletServiceEventListener(in WalletServiceEventListenerRequest request);
+}
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl b/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl
new file mode 100644
index 0000000..f37b930
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.service.quickaccesswallet.GetWalletCardsError;
+import android.service.quickaccesswallet.GetWalletCardsResponse;
+import android.service.quickaccesswallet.WalletServiceEvent;
+
+/**
+ * Interface to receive the result of requests to the wallet application.
+ *
+ * @hide
+ */
+interface IQuickAccessWalletServiceCallbacks {
+    // Called in response to onWalletCardsRequested on success. May only be called once per request.
+    oneway void onGetWalletCardsSuccess(in GetWalletCardsResponse response);
+    // Called in response to onWalletCardsRequested when an error occurs. May only be called once
+    // per request.
+    oneway void onGetWalletCardsFailure(in GetWalletCardsError error);
+    // Called in response to registerWalletServiceEventListener. May be called multiple times as
+    // long as the event listener is registered.
+    oneway void onWalletServiceEvent(in WalletServiceEvent event);
+}
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
new file mode 100644
index 0000000..cfc6d57
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.function.Consumer;
+
+/**
+ * Facilitates accessing cards from the {@link QuickAccessWalletService}.
+ *
+ * @hide
+ */
+public interface QuickAccessWalletClient {
+
+    /**
+     * Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the
+     * service is unavailable, {@link #isWalletServiceAvailable()} will return false.
+     */
+    @NonNull
+    static QuickAccessWalletClient create(@NonNull Context context) {
+        return new QuickAccessWalletClientImpl(context);
+    }
+
+    /**
+     * @return true if the {@link QuickAccessWalletService} is available.
+     */
+    boolean isWalletServiceAvailable();
+
+    /**
+     * Get wallet cards from the {@link QuickAccessWalletService}.
+     */
+    void getWalletCards(
+            @NonNull GetWalletCardsRequest request,
+            @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
+            @NonNull Consumer<GetWalletCardsError> onFailureListener);
+
+    /**
+     * Notify the {@link QuickAccessWalletService} service that a wallet card was selected.
+     */
+    void selectWalletCard(@NonNull SelectWalletCardRequest request);
+
+    /**
+     * Notify the {@link QuickAccessWalletService} service that the Wallet was dismissed.
+     */
+    void notifyWalletDismissed();
+
+    /**
+     * Unregister event listener.
+     */
+    void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+
+    /**
+     * Unregister event listener
+     */
+    void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+
+    /**
+     * The manifest entry for the QuickAccessWalletService may also publish information about the
+     * activity that hosts the Wallet view. This is typically the home screen of the Wallet
+     * application.
+     */
+    @Nullable
+    Intent getWalletActivity();
+
+    /**
+     * The manifest entry for the {@link QuickAccessWalletService} may publish the activity that
+     * hosts the settings
+     */
+    @Nullable
+    Intent getSettingsActivity();
+}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
new file mode 100644
index 0000000..17c287f
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * @hide
+ */
+@SuppressWarnings("AndroidJdkLibsChecker")
+class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Callback,
+        ServiceConnection {
+
+    private static final String TAG = "QAWalletSClient";
+    private final Handler mHandler;
+    private final Context mContext;
+    private final Queue<ApiCaller> mRequestQueue;
+    private final Map<Consumer<WalletServiceEvent>, String> mEventListeners;
+    private boolean mIsConnected;
+    @Nullable
+    private IQuickAccessWalletService mService;
+
+
+    @Nullable
+    private final QuickAccessWalletServiceInfo mServiceInfo;
+
+    private static final int MSG_CONNECT = 1;
+    private static final int MSG_CONNECTED = 2;
+    private static final int MSG_EXECUTE = 3;
+    private static final int MSG_DISCONNECT = 4;
+
+    QuickAccessWalletClientImpl(@NonNull Context context) {
+        mContext = context.getApplicationContext();
+        mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
+        mHandler = new Handler(Looper.getMainLooper(), this);
+        mRequestQueue = new LinkedList<>();
+        mEventListeners = new HashMap<>(1);
+    }
+
+    @Override
+    public boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_CONNECT:
+                connectInternal();
+                break;
+            case MSG_CONNECTED:
+                onConnectedInternal((IQuickAccessWalletService) msg.obj);
+                break;
+            case MSG_EXECUTE:
+                executeInternal((ApiCaller) msg.obj);
+                break;
+            case MSG_DISCONNECT:
+                disconnectInternal();
+                break;
+            default:
+                Log.w(TAG, "Unknown what: " + msg.what);
+                return false;
+        }
+        return true;
+    }
+
+    private void connect() {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT));
+    }
+
+    private void connectInternal() {
+        if (mServiceInfo == null) {
+            Log.w(TAG, "Wallet service unavailable");
+            return;
+        }
+        if (mIsConnected) {
+            Log.w(TAG, "already connected");
+            return;
+        }
+        mIsConnected = true;
+        Intent intent = new Intent(SERVICE_INTERFACE);
+        intent.setComponent(mServiceInfo.getComponentName());
+        int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
+        mContext.bindService(intent, this, flags);
+    }
+
+    private void onConnectedInternal(IQuickAccessWalletService service) {
+        if (!mIsConnected) {
+            Log.w(TAG, "onConnectInternal but connection closed");
+            mService = null;
+            return;
+        }
+        mService = service;
+        for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
+            try {
+                apiCaller.performApiCall(mService);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onConnectedInternal error", e);
+                apiCaller.onApiError();
+                disconnect();
+                break;
+            }
+            mRequestQueue.remove(apiCaller);
+        }
+    }
+
+    private void disconnect() {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
+    }
+
+    private void disconnectInternal() {
+        if (!mIsConnected) {
+            Log.w(TAG, "already disconnected");
+            return;
+        }
+        mIsConnected = false;
+        mContext.unbindService(/*conn=*/this);
+        mService = null;
+        mEventListeners.clear();
+        mRequestQueue.clear();
+    }
+
+    private void execute(ApiCaller apiCaller) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, apiCaller));
+    }
+
+    private void executeInternal(ApiCaller apiCall) {
+        if (mIsConnected && mService != null) {
+            try {
+                apiCall.performApiCall(mService);
+            } catch (RemoteException e) {
+                Log.w(TAG, "executeInternal error", e);
+                apiCall.onApiError();
+                disconnect();
+            }
+        } else {
+            mRequestQueue.add(apiCall);
+            connect();
+        }
+    }
+
+    public boolean isWalletServiceAvailable() {
+        return mServiceInfo != null;
+    }
+
+    private abstract static class ApiCaller {
+        abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
+
+        void onApiError() {
+            Log.w(TAG, "api error");
+        }
+    }
+
+    public void getWalletCards(
+            @NonNull GetWalletCardsRequest request,
+            @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
+            @NonNull Consumer<GetWalletCardsError> onFailureListener) {
+
+        BaseCallbacks callback = new BaseCallbacks() {
+            @Override
+            public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
+                mHandler.post(() -> onSuccessListener.accept(response));
+            }
+
+            @Override
+            public void onGetWalletCardsFailure(GetWalletCardsError error) {
+                mHandler.post(() -> onFailureListener.accept(error));
+            }
+        };
+
+        execute(new ApiCaller() {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletCardsRequested(request, callback);
+            }
+
+            @Override
+            public void onApiError() {
+                callback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
+            }
+        });
+    }
+
+    public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
+        execute(new ApiCaller() {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletCardSelected(request);
+            }
+        });
+    }
+
+    public void notifyWalletDismissed() {
+        execute(new ApiCaller() {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletDismissed();
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
+            }
+        });
+    }
+
+    @Override
+    public void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
+
+        BaseCallbacks callback = new BaseCallbacks() {
+            @Override
+            public void onWalletServiceEvent(WalletServiceEvent event) {
+                Log.i(TAG, "onWalletServiceEvent");
+                mHandler.post(() -> listener.accept(event));
+            }
+        };
+
+        execute(new ApiCaller() {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                String listenerId = UUID.randomUUID().toString();
+                WalletServiceEventListenerRequest request =
+                        new WalletServiceEventListenerRequest(listenerId);
+                mEventListeners.put(listener, listenerId);
+                service.registerWalletServiceEventListener(request, callback);
+            }
+        });
+    }
+
+    @Override
+    public void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
+        execute(new ApiCaller() {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                String listenerId = mEventListeners.get(listener);
+                if (listenerId == null) {
+                    return;
+                }
+                WalletServiceEventListenerRequest request =
+                        new WalletServiceEventListenerRequest(listenerId);
+                service.unregisterWalletServiceEventListener(request);
+            }
+        });
+    }
+
+    @Override
+    @Nullable
+    public Intent getWalletActivity() {
+        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
+            return null;
+        }
+        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
+                .setComponent(
+                        new ComponentName(
+                                mServiceInfo.getComponentName().getPackageName(),
+                                mServiceInfo.getWalletActivity()));
+    }
+
+    @Override
+    @Nullable
+    public Intent getSettingsActivity() {
+        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
+            return null;
+        }
+        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
+                .setComponent(
+                        new ComponentName(
+                                mServiceInfo.getComponentName().getPackageName(),
+                                mServiceInfo.getSettingsActivity()));
+    }
+
+    /**
+     * Connection to the {@link QuickAccessWalletService}
+     */
+
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder binder) {
+        IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, service));
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        // Do not disconnect, as we may later be re-connected
+        Log.w(TAG, "onServiceDisconnected");
+    }
+
+    @Override
+    public void onBindingDied(ComponentName name) {
+        // This is a recoverable error but the client will need to reconnect.
+        Log.w(TAG, "onBindingDied");
+        disconnect();
+    }
+
+    @Override
+    public void onNullBinding(ComponentName name) {
+        Log.w(TAG, "onNullBinding");
+        disconnect();
+    }
+
+    private static class BaseCallbacks extends IQuickAccessWalletServiceCallbacks.Stub {
+        public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
+            throw new IllegalStateException();
+        }
+
+        public void onGetWalletCardsFailure(GetWalletCardsError error) {
+            throw new IllegalStateException();
+        }
+
+        public void onWalletServiceEvent(WalletServiceEvent event) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
new file mode 100644
index 0000000..d968405
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+
+/**
+ * A {@code QuickAccessWalletService} provides a list of {@code WalletCard}s shown in the Quick
+ * Access Wallet. The Quick Access Wallet allows the user to change their selected payment method
+ * and access other important passes, such as tickets and transit passes, without leaving the
+ * context of their current app.
+ *
+ * <p>An {@code QuickAccessWalletService} is only bound to the Android System for the purposes of
+ * showing wallet cards if:
+ * <ol>
+ *   <li>The application hosting the QuickAccessWalletService is also the default NFC payment
+ *   application. This means that the same application must also have a
+ *   {@link android.nfc.cardemulation.HostApduService} or
+ *   {@link android.nfc.cardemulation.OffHostApduService} that requires the
+ *   android.permission.BIND_NFC_SERVICE permission.
+ *   <li>The user explicitly selected the application as the default payment application in
+ *   the Tap &amp; pay settings screen.
+ *   <li>The application requires the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
+ *   permission in its manifest.
+ *   <li>The user explicitly enables it using Android Settings (the
+ *       {@link Settings#ACTION_QUICK_ACCESS_WALLET_SETTINGS} intent can be used to launch it).
+ * </ol>
+ *
+ * <a name="BasicUsage"></a>
+ * <h3>Basic usage</h3>
+ *
+ * <p>The basic Quick Access Wallet process is defined by the workflow below:
+ * <ol>
+ *   <li>User performs a gesture to bring up the Quick Access Wallet, which is displayed by the
+ *   Android System.
+ *   <li>The Android System creates a {@link GetWalletCardsRequest}, binds to the
+ *   {@link QuickAccessWalletService}, and delivers the request.
+ *   <li>The service receives the request through {@link #onWalletCardsRequested}
+ *   <li>The service responds by calling {@link GetWalletCardsCallback#onSuccess} with a
+ *   {@link GetWalletCardsResponse response} that contains between 1 and
+ *   {@link GetWalletCardsRequest#getMaxCards() maxCards} cards.
+ *   <li>The Android System displays the Quick Access Wallet containing the provided cards. The
+ *   card at the {@link GetWalletCardsResponse#getSelectedIndex() selectedIndex} will initially
+ *   be presented as the 'selected' card.
+ *   <li>As soon as the cards are displayed, the Android System will notify the service that the
+ *   card at the selected index has been selected through {@link #onWalletCardSelected}.
+ *   <li>The user interacts with the wallet and may select one or more cards in sequence. Each time
+ *   a new card is selected, the Android System will notify the service through
+ *   {@link #onWalletCardSelected} and will provide the {@link WalletCard#getCardId() cardId} of the
+ *   card that is now selected.
+ *   <li>When the wallet is dismissed, the Android System will notify the service through
+ *   {@link #onWalletDismissed}.
+ * </ol>
+ *
+ * <p>The workflow is designed to minimize the time that the Android System is bound to the
+ * service, but connections may be cached and reused to improve performance and conserve memory.
+ * All calls should be considered stateless: if the service needs to keep state between calls, it
+ * must do its own state management (keeping in mind that the service's process might be killed
+ * by the Android System when unbound; for example, if the device is running low in memory).
+ *
+ * <p>
+ * <a name="ErrorHandling"></a>
+ * <h3>Error handling</h3>
+ * <p>If the service encountered an error processing the request, it should call
+ * {@link GetWalletCardsCallback#onFailure}.
+ * For performance reasons, it's paramount that the service calls either
+ * {@link GetWalletCardsCallback#onSuccess} or
+ * {@link GetWalletCardsCallback#onFailure} for each
+ * {@link #onWalletCardsRequested} received - if it doesn't, the request will eventually time out
+ * and be discarded by the Android System.
+ *
+ * <p>
+ * <a name="ManifestEntry"></a>
+ * <h3>Manifest entry</h3>
+ *
+ * <p>QuickAccessWalletService must require the permission
+ * "android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE".
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * <service
+ *     android:name=".MyQuickAccessWalletService"
+ *     android:label="@string/my_default_tile_label"
+ *     android:icon="@drawable/my_default_icon_label"
+ *     android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
+ *     <intent-filter>
+ *         <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
+ *     </intent-filter>
+ *     <meta-data android:name="android.quickaccesswallet"
+ *          android:resource="@xml/quickaccesswallet_configuration" />;
+ * </service>}
+ * </pre>
+ * <p>
+ * The {@literal <meta-data>} element includes an android:resource attribute that points to an
+ * XML resource with further details about the service. The {@code quickaccesswallet_configuration}
+ * in the example above specifies an activity that allows the users to view the entire wallet.
+ * The following example shows the quickaccesswallet_configuration XML resource:
+ * <p>
+ * <pre class="prettyprint">
+ * {@literal
+ * <quickaccesswallet-service
+ *   xmlns:android="http://schemas.android.com/apk/res/android"
+ *   android:settingsActivity="com.example.android.SettingsActivity"
+ *   android:targetActivity="com.example.android.WalletActivity"/>
+ * }
+ * </pre>
+ *
+ * <p>The entry for {@code settingsActivity} should contain the fully qualified class name of an
+ * activity that allows the user to modify the settings for this service. The {@code targetActivity}
+ * entry should contain the fully qualified class name of an activity that allows the user to view
+ * their entire wallet. If specified, the wallet activity will be started with the Intent action
+ * {@link #ACTION_VIEW_WALLET} and the settings activity will be started with the Intent action
+ * {@link #ACTION_VIEW_WALLET_SETTINGS}.
+ */
+public abstract class QuickAccessWalletService extends Service {
+
+    private static final String TAG = "QAWalletService";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the
+     * {@link android.Manifest.permission#BIND_QUICK_ACCESS_WALLET_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.quickaccesswallet.QuickAccessWalletService";
+
+    /**
+     * Intent action to launch an activity to display the wallet.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW_WALLET =
+            "android.service.quickaccesswallet.action.VIEW_WALLET";
+
+    /**
+     * Intent action to launch an activity to display quick access wallet settings.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW_WALLET_SETTINGS =
+            "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
+
+    /**
+     * Broadcast Action: Sent by the wallet application to dismiss the Quick Access Wallet.
+     * <p>
+     * The Quick Access Wallet may be shown in a system window on top of other Activities. If the
+     * user selects a payment card from the Quick Access Wallet and then holds their phone to an NFC
+     * terminal, the wallet application will need to show a payment Activity. But if the Quick
+     * Access Wallet is still being shown, it may obscure the payment Activity. To avoid this, the
+     * wallet application can send a broadcast to the Android System with this action to request
+     * that the Quick Access Wallet be dismissed.
+     * <p>
+     * This broadcast must use the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
+     * permission to ensure that it is only delivered to System UI. Furthermore, your application
+     * must require the {@code android.permission.DISMISS_QUICK_ACCESS_WALLET}
+     * <p>
+     * <pre class="prettyprint">
+     * context.sendBroadcast(
+     *     new Intent(ACTION_DISMISS_WALLET), Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE);
+     * </pre>
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DISMISS_WALLET =
+            "android.service.quickaccesswallet.action.DISMISS_WALLET";
+
+    /**
+     * Name under which a QuickAccessWalletService component publishes information about itself.
+     * This meta-data should reference an XML resource containing a
+     * <code>&lt;{@link
+     * android.R.styleable#QuickAccessWalletService quickaccesswallet-service}&gt;</code> tag. This
+     * is a a sample XML file configuring an QuickAccessWalletService:
+     * <pre> &lt;quickaccesswallet-service
+     *     android:walletActivity="foo.bar.WalletActivity"
+     *     . . .
+     * /&gt;</pre>
+     */
+    public static final String SERVICE_META_DATA = "android.quickaccesswallet";
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    @Nullable
+    private String mEventListenerId;
+    @Nullable
+    private IQuickAccessWalletServiceCallbacks mEventListener;
+
+    private final IQuickAccessWalletService mInterface = new IQuickAccessWalletService.Stub() {
+        @Override
+        public void onWalletCardsRequested(
+                @NonNull GetWalletCardsRequest request,
+                @NonNull IQuickAccessWalletServiceCallbacks callback) {
+            mHandler.post(() -> onWalletCardsRequestedInternal(request, callback));
+        }
+
+        @Override
+        public void onWalletCardSelected(@NonNull SelectWalletCardRequest request) {
+            mHandler.post(() -> QuickAccessWalletService.this.onWalletCardSelected(request));
+        }
+
+        @Override
+        public void onWalletDismissed() {
+            mHandler.post(QuickAccessWalletService.this::onWalletDismissed);
+        }
+
+        public void registerWalletServiceEventListener(
+                @NonNull WalletServiceEventListenerRequest request,
+                @NonNull IQuickAccessWalletServiceCallbacks callback) {
+            mHandler.post(() -> registerDismissWalletListenerInternal(request, callback));
+        }
+
+        public void unregisterWalletServiceEventListener(
+                @NonNull WalletServiceEventListenerRequest request) {
+            mHandler.post(() -> unregisterDismissWalletListenerInternal(request));
+        }
+    };
+
+    private void onWalletCardsRequestedInternal(
+            GetWalletCardsRequest request,
+            IQuickAccessWalletServiceCallbacks callback) {
+        onWalletCardsRequested(request, new GetWalletCardsCallback(callback, mHandler));
+    }
+
+    @Override
+    @Nullable
+    public IBinder onBind(@NonNull Intent intent) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            // Binding to the QuickAccessWalletService is protected by the
+            // android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission, which is defined in
+            // R. Pre-R devices can have other side-loaded applications that claim this permission.
+            // This ensures that the service is only available when properly permission protected.
+            Log.w(TAG, "Warning: binding on pre-R device");
+        }
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+        return null;
+    }
+
+    /**
+     * Called when the user requests the service to provide wallet cards.
+     *
+     * <p>This method will be called on the main thread, but the callback may be called from any
+     * thread. The callback should be called as quickly as possible. The service must always call
+     * either {@link GetWalletCardsCallback#onSuccess(GetWalletCardsResponse)} or {@link
+     * GetWalletCardsCallback#onFailure(GetWalletCardsError)}. Calling multiple times or calling
+     * both methods will cause an exception to be thrown.
+     */
+    public abstract void onWalletCardsRequested(
+            @NonNull GetWalletCardsRequest request,
+            @NonNull GetWalletCardsCallback callback);
+
+    /**
+     * A wallet card was selected. Sent when the user selects a wallet card from the list of cards.
+     * Selection may indicate that the card is now in the center of the screen, or highlighted in
+     * some other fashion. It does not mean that the user clicked on the card -- clicking on the
+     * card will cause the {@link WalletCard#getPendingIntent()} to be sent.
+     *
+     * <p>Card selection events are especially important to NFC payment applications because
+     * many NFC terminals can only accept one payment card at a time. If the user has several NFC
+     * cards in their wallet, selecting different cards can change which payment method is presented
+     * to the terminal.
+     */
+    public abstract void onWalletCardSelected(@NonNull SelectWalletCardRequest request);
+
+    /**
+     * Indicates that the wallet was dismissed. This is received when the Quick Access Wallet is no
+     * longer visible.
+     */
+    public abstract void onWalletDismissed();
+
+    /**
+     * Send a {@link WalletServiceEvent} to the Quick Access Wallet.
+     * <p>
+     * Background events may require that the Quick Access Wallet view be updated. For example, if
+     * the wallet application hosting this service starts to handle an NFC payment while the Quick
+     * Access Wallet is being shown, the Quick Access Wallet will need to be dismissed so that the
+     * Activity showing the payment can be displayed to the user.
+     */
+    public final void sendWalletServiceEvent(@NonNull WalletServiceEvent serviceEvent) {
+        mHandler.post(() -> sendWalletServiceEventInternal(serviceEvent));
+    }
+
+    private void sendWalletServiceEventInternal(WalletServiceEvent serviceEvent) {
+        if (mEventListener == null) {
+            Log.i(TAG, "No dismiss listener registered");
+            return;
+        }
+        try {
+            mEventListener.onWalletServiceEvent(serviceEvent);
+        } catch (RemoteException e) {
+            Log.w(TAG, "onWalletServiceEvent error", e);
+            mEventListenerId = null;
+            mEventListener = null;
+        }
+    }
+
+    private void registerDismissWalletListenerInternal(
+            @NonNull WalletServiceEventListenerRequest request,
+            @NonNull IQuickAccessWalletServiceCallbacks callback) {
+        mEventListenerId = request.getListenerId();
+        mEventListener = callback;
+    }
+
+    private void unregisterDismissWalletListenerInternal(
+            @NonNull WalletServiceEventListenerRequest request) {
+        if (mEventListenerId != null && mEventListenerId.equals(request.getListenerId())) {
+            mEventListenerId = null;
+            mEventListener = null;
+        } else {
+            Log.w(TAG, "dismiss listener missing or replaced");
+        }
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
new file mode 100644
index 0000000..8793f28
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * {@link ServiceInfo} and meta-data about a {@link QuickAccessWalletService}.
+ *
+ * @hide
+ */
+class QuickAccessWalletServiceInfo {
+
+    private static final String TAG = "QAWalletSInfo";
+    private static final String TAG_WALLET_SERVICE = "quickaccesswallet-service";
+
+    private final ServiceInfo mServiceInfo;
+    private final ServiceMetadata mServiceMetadata;
+
+    private QuickAccessWalletServiceInfo(
+            @NonNull ServiceInfo serviceInfo,
+            @NonNull ServiceMetadata metadata) {
+        mServiceInfo = serviceInfo;
+        mServiceMetadata = metadata;
+    }
+
+    @Nullable
+    static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
+        ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
+        if (defaultPaymentApp == null) {
+            Log.d(TAG, "create: default payment app not set");
+            return null;
+        }
+
+        ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName());
+        if (serviceInfo == null) {
+            Log.d(TAG, "create: unable to resolve service intent");
+            return null;
+        }
+
+        if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) {
+            Log.w(TAG, String.format("QuickAccessWalletService from %s does not have permission %s",
+                    serviceInfo.packageName, Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
+            return null;
+        }
+
+        ServiceMetadata metadata = parseServiceMetadata(context, serviceInfo);
+        return new QuickAccessWalletServiceInfo(serviceInfo, metadata);
+    }
+
+    private static ComponentName getDefaultPaymentApp(Context context) {
+        ContentResolver cr = context.getContentResolver();
+        String comp = Settings.Secure.getString(cr, Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
+        return comp == null ? null : ComponentName.unflattenFromString(comp);
+    }
+
+    private static ServiceInfo getWalletServiceInfo(Context context, String packageName) {
+        Intent intent = new Intent(QuickAccessWalletService.SERVICE_INTERFACE);
+        intent.setPackage(packageName);
+        List<ResolveInfo> resolveInfos =
+                context.getPackageManager().queryIntentServices(intent,
+                        PackageManager.MATCH_DEFAULT_ONLY);
+        return resolveInfos.isEmpty() ? null : resolveInfos.get(0).serviceInfo;
+    }
+
+    private static class ServiceMetadata {
+        @Nullable
+        private final String mSettingsActivity;
+        @Nullable
+        private final String mWalletActivity;
+
+        private ServiceMetadata(String settingsActivity, String walletActivity) {
+            this.mSettingsActivity = settingsActivity;
+            this.mWalletActivity = walletActivity;
+        }
+    }
+
+    private static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) {
+        PackageManager pm = context.getPackageManager();
+        final XmlResourceParser parser =
+                serviceInfo.loadXmlMetaData(pm, QuickAccessWalletService.SERVICE_META_DATA);
+
+        if (parser == null) {
+            return new ServiceMetadata(null, null);
+        }
+
+        try {
+            Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+            int type = 0;
+            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+                type = parser.next();
+            }
+
+            if (TAG_WALLET_SERVICE.equals(parser.getName())) {
+                final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+                TypedArray afsAttributes = null;
+                try {
+                    afsAttributes = resources.obtainAttributes(allAttributes,
+                            R.styleable.QuickAccessWalletService);
+                    String settingsActivity = afsAttributes.getString(
+                            R.styleable.QuickAccessWalletService_settingsActivity);
+                    String walletActivity = afsAttributes.getString(
+                            R.styleable.QuickAccessWalletService_targetActivity);
+                    return new ServiceMetadata(settingsActivity, walletActivity);
+                } finally {
+                    if (afsAttributes != null) {
+                        afsAttributes.recycle();
+                    }
+                }
+            } else {
+                Log.e(TAG, "Meta-data does not start with quickaccesswallet-service tag");
+            }
+
+        } catch (PackageManager.NameNotFoundException
+                | IOException
+                | XmlPullParserException e) {
+            Log.e(TAG, "Error parsing quickaccesswallet service meta-data", e);
+        }
+        return new ServiceMetadata(null, null);
+    }
+
+    /**
+     * @return the component name of the {@link QuickAccessWalletService}
+     */
+    @NonNull
+    ComponentName getComponentName() {
+        return mServiceInfo.getComponentName();
+    }
+
+    /**
+     * @return the fully qualified name of the activity that hosts the full wallet. If available,
+     * this intent should be started with the action
+     * {@link QuickAccessWalletService#ACTION_VIEW_WALLET}
+     */
+    @Nullable
+    String getWalletActivity() {
+        return mServiceMetadata.mWalletActivity;
+    }
+
+    /**
+     * @return the fully qualified name of the activity that allows the user to change quick access
+     * wallet settings. If available, this intent should be started with the action {@link
+     * QuickAccessWalletService#ACTION_VIEW_WALLET_SETTINGS}
+     */
+    @Nullable
+    String getSettingsActivity() {
+        return mServiceMetadata.mSettingsActivity;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.aidl b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.aidl
new file mode 100644
index 0000000..97a0d41
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable SelectWalletCardRequest;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.java b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.java
new file mode 100644
index 0000000..cb69eee
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a request to a {@link QuickAccessWalletService} to select a particular {@link
+ * WalletCard walletCard}. Card selection events are transmitted to the WalletService so that the
+ * selected card may be used by the NFC payment service.
+ */
+public final class SelectWalletCardRequest implements Parcelable {
+
+    private final String mCardId;
+
+    /**
+     * Creates a new GetWalletCardsRequest.
+     *
+     * @param cardId The {@link WalletCard#getCardId() cardId} of the wallet card that is currently
+     *               selected.
+     */
+    public SelectWalletCardRequest(@NonNull String cardId) {
+        this.mCardId = cardId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mCardId);
+    }
+
+    @NonNull
+    public static final Creator<SelectWalletCardRequest> CREATOR =
+            new Creator<SelectWalletCardRequest>() {
+                @Override
+                public SelectWalletCardRequest createFromParcel(Parcel source) {
+                    String cardId = source.readString();
+                    return new SelectWalletCardRequest(cardId);
+                }
+
+                @Override
+                public SelectWalletCardRequest[] newArray(int size) {
+                    return new SelectWalletCardRequest[size];
+                }
+            };
+
+    /**
+     * The {@link WalletCard#getCardId() cardId} of the wallet card that is currently selected.
+     */
+    @NonNull
+    public String getCardId() {
+        return mCardId;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.aidl b/core/java/android/service/quickaccesswallet/WalletCard.aidl
new file mode 100644
index 0000000..115213d
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/WalletCard.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable WalletCard;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
new file mode 100644
index 0000000..c3b1a4b
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * A {@link WalletCard} can represent anything that a user might carry in their wallet -- a credit
+ * card, library card, transit pass, etc. Cards are identified by a String identifier and contain a
+ * card image, card image content description, and a {@link PendingIntent} to be used if the user
+ * clicks on the card. Cards may be displayed with an icon and label, though these are optional.
+ */
+public final class WalletCard implements Parcelable {
+
+    private final String mCardId;
+    private final Icon mCardImage;
+    private final CharSequence mContentDescription;
+    private final PendingIntent mPendingIntent;
+    private final Icon mCardIcon;
+    private final CharSequence mCardLabel;
+
+    private WalletCard(Builder builder) {
+        this.mCardId = builder.mCardId;
+        this.mCardImage = builder.mCardImage;
+        this.mContentDescription = builder.mContentDescription;
+        this.mPendingIntent = builder.mPendingIntent;
+        this.mCardIcon = builder.mCardIcon;
+        this.mCardLabel = builder.mCardLabel;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mCardId);
+        mCardImage.writeToParcel(dest, flags);
+        TextUtils.writeToParcel(mContentDescription, dest, flags);
+        PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
+        if (mCardIcon == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            mCardIcon.writeToParcel(dest, flags);
+        }
+        TextUtils.writeToParcel(mCardLabel, dest, flags);
+    }
+
+    private static WalletCard readFromParcel(Parcel source) {
+        String cardId = source.readString();
+        Icon cardImage = Icon.CREATOR.createFromParcel(source);
+        CharSequence contentDesc = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        PendingIntent pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source);
+        Icon cardIcon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source);
+        CharSequence cardLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        return new Builder(cardId, cardImage, contentDesc, pendingIntent)
+                .setCardIcon(cardIcon)
+                .setCardLabel(cardLabel)
+                .build();
+    }
+
+    @NonNull
+    public static final Creator<WalletCard> CREATOR =
+            new Creator<WalletCard>() {
+                @Override
+                public WalletCard createFromParcel(Parcel source) {
+                    return readFromParcel(source);
+                }
+
+                @Override
+                public WalletCard[] newArray(int size) {
+                    return new WalletCard[size];
+                }
+            };
+
+    /**
+     * The card id must be unique within the list of cards returned.
+     */
+    @NonNull
+    public String getCardId() {
+        return mCardId;
+    }
+
+    /**
+     * The visual representation of the card. If the card image Icon is a bitmap, it should have a
+     * width of {@link GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
+     * GetWalletCardsRequest#getCardHeightPx()}.
+     */
+    @NonNull
+    public Icon getCardImage() {
+        return mCardImage;
+    }
+
+    /**
+     * The content description of the card image.
+     */
+    @NonNull
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * If the user performs a click on the card, this PendingIntent will be sent. If the device is
+     * locked, the wallet will first request device unlock before sending the pending intent.
+     */
+    @NonNull
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    /**
+     * An icon may be shown alongside the card image to convey information about how the card can be
+     * used, or if some other action must be taken before using the card. For example, an NFC logo
+     * could indicate that the card is NFC-enabled and will be provided to an NFC terminal if the
+     * phone is held in close proximity to the NFC reader.
+     *
+     * <p>If the supplied Icon is backed by a bitmap, it should have width and height
+     * {@link GetWalletCardsRequest#getIconSizePx()}.
+     */
+    @Nullable
+    public Icon getCardIcon() {
+        return mCardIcon;
+    }
+
+    /**
+     * A card label may be shown alongside the card image to convey information about how the card
+     * can be used, or if some other action must be taken before using the card. For example, an
+     * NFC-enabled card could be labeled "Hold near reader" to inform the user of how to use NFC
+     * cards when interacting with an NFC reader.
+     *
+     * <p>If the provided label is too long to fit on one line, it may be truncated and ellipsized.
+     */
+    @Nullable
+    public CharSequence getCardLabel() {
+        return mCardLabel;
+    }
+
+    /**
+     * Builder for {@link WalletCard} objects. You must to provide cardId, cardImage,
+     * contentDescription, and pendingIntent. If the card is opaque and should be shown with
+     * elevation, set hasShadow to true. cardIcon and cardLabel are optional.
+     */
+    public static final class Builder {
+        private String mCardId;
+        private Icon mCardImage;
+        private CharSequence mContentDescription;
+        private PendingIntent mPendingIntent;
+        private Icon mCardIcon;
+        private CharSequence mCardLabel;
+
+        /**
+         * @param cardId             The card id must be non-null and unique within the list of
+         *                           cards returned. <b>Note:
+         *                           </b> this card ID should <b>not</b> contain PII (Personally
+         *                           Identifiable Information, * such as username or email
+         *                           address).
+         * @param cardImage          The visual representation of the card. If the card image Icon
+         *                           is a bitmap, it should have a width of {@link
+         *                           GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
+         *                           GetWalletCardsRequest#getCardHeightPx()}. If the card image
+         *                           does not have these dimensions, it may appear distorted when it
+         *                           is scaled to fit these dimensions on screen.
+         * @param contentDescription The content description of the card image. This field is
+         *                           required.
+         *                           <b>Note: </b> this message should <b>not</b> contain PII
+         *                           (Personally Identifiable Information, such as username or email
+         *                           address).
+         * @param pendingIntent      If the user performs a click on the card, this PendingIntent
+         *                           will be sent. If the device is locked, the wallet will first
+         *                           request device unlock before sending the pending intent.
+         */
+        public Builder(@NonNull String cardId,
+                @NonNull Icon cardImage,
+                @NonNull CharSequence contentDescription,
+                @NonNull PendingIntent pendingIntent) {
+            mCardId = cardId;
+            mCardImage = cardImage;
+            mContentDescription = contentDescription;
+            mPendingIntent = pendingIntent;
+        }
+
+        /**
+         * An icon may be shown alongside the card image to convey information about how the card
+         * can be used, or if some other action must be taken before using the card. For example, an
+         * NFC logo could indicate that the card is NFC-enabled and will be provided to an NFC
+         * terminal if the phone is held in close proximity to the NFC reader. This field is
+         * optional.
+         *
+         * <p>If the supplied Icon is backed by a bitmap, it should have width and height
+         * {@link GetWalletCardsRequest#getIconSizePx()}.
+         */
+        @NonNull
+        public Builder setCardIcon(@Nullable Icon cardIcon) {
+            mCardIcon = cardIcon;
+            return this;
+        }
+
+        /**
+         * A card label may be shown alongside the card image to convey information about how the
+         * card can be used, or if some other action must be taken before using the card. For
+         * example, an NFC-enabled card could be labeled "Hold near reader" to inform the user of
+         * how to use NFC cards when interacting with an NFC reader. This field is optional.
+         * <b>Note: </b> this card label should <b>not</b> contain PII (Personally Identifiable
+         * Information, such as username or email address). If the provided label is too long to fit
+         * on one line, it may be truncated and ellipsized.
+         */
+        @NonNull
+        public Builder setCardLabel(@Nullable CharSequence cardLabel) {
+            mCardLabel = cardLabel;
+            return this;
+        }
+
+        /**
+         * Builds a new {@link WalletCard} instance.
+         *
+         * @return A built response.
+         */
+        @NonNull
+        public WalletCard build() {
+            return new WalletCard(this);
+        }
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.aidl b/core/java/android/service/quickaccesswallet/WalletServiceEvent.aidl
new file mode 100644
index 0000000..891cf1d
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable WalletServiceEvent;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
new file mode 100644
index 0000000..fb524be
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a request from the {@link QuickAccessWalletService wallet app} to the Quick Access
+ * Wallet in System UI. Background events may necessitate that the Quick Access Wallet update its
+ * view. For example, if the wallet application handles an NFC payment while the Quick Access Wallet
+ * is being shown, it needs to tell the Quick Access Wallet so that the wallet can be dismissed and
+ * Activity showing the payment can be displayed to the user.
+ */
+public final class WalletServiceEvent implements Parcelable {
+
+    /**
+     * An NFC payment has started. If the Quick Access Wallet is in a system window, it will need to
+     * be dismissed so that an Activity showing the payment can be displayed.
+     */
+    public static final int TYPE_NFC_PAYMENT_STARTED = 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TYPE_NFC_PAYMENT_STARTED})
+    public @interface EventType {
+    }
+
+    @EventType
+    private final int mEventType;
+
+    /**
+     * Creates a new DismissWalletRequest.
+     */
+    public WalletServiceEvent(@EventType int eventType) {
+        this.mEventType = eventType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mEventType);
+    }
+
+    @NonNull
+    public static final Creator<WalletServiceEvent> CREATOR =
+            new Creator<WalletServiceEvent>() {
+                @Override
+                public WalletServiceEvent createFromParcel(Parcel source) {
+                    int eventType = source.readInt();
+                    return new WalletServiceEvent(eventType);
+                }
+
+                @Override
+                public WalletServiceEvent[] newArray(int size) {
+                    return new WalletServiceEvent[size];
+                }
+            };
+
+    /**
+     * @return the event type
+     */
+    @EventType
+    public int getEventType() {
+        return mEventType;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.aidl b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.aidl
new file mode 100644
index 0000000..155f92e
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+parcelable WalletServiceEventListenerRequest;
\ No newline at end of file
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.java b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.java
new file mode 100644
index 0000000..223110e
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Register a dismiss request listener with the QuickAccessWalletService. This allows the service to
+ * dismiss the wallet if it needs to show a payment activity in response to an NFC event.
+ *
+ * @hide
+ */
+public final class WalletServiceEventListenerRequest implements Parcelable {
+
+    private final String mListenerId;
+
+    /**
+     * Construct a new {@code DismissWalletListenerRequest}.
+     *
+     * @param listenerKey A unique key that identifies the listener.
+     */
+    public WalletServiceEventListenerRequest(@NonNull String listenerKey) {
+        mListenerId = listenerKey;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mListenerId);
+    }
+
+    private static WalletServiceEventListenerRequest readFromParcel(Parcel source) {
+        String listenerId = source.readString();
+        return new WalletServiceEventListenerRequest(listenerId);
+    }
+
+    @NonNull
+    public static final Creator<WalletServiceEventListenerRequest> CREATOR =
+            new Creator<WalletServiceEventListenerRequest>() {
+                @Override
+                public WalletServiceEventListenerRequest createFromParcel(Parcel source) {
+                    return readFromParcel(source);
+                }
+
+                @Override
+                public WalletServiceEventListenerRequest[] newArray(int size) {
+                    return new WalletServiceEventListenerRequest[size];
+                }
+            };
+
+    /**
+     * Returns the unique key that identifies the wallet dismiss request listener.
+     */
+    @NonNull
+    public String getListenerId() {
+        return mListenerId;
+    }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 490c477..9232a91 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3226,6 +3226,13 @@
     <permission android:name="android.permission.BIND_NFC_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by a {@link android.service.quickaccesswallet.QuickAccessWalletService}
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it.
          @hide -->
     <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e0d8492..940e9f1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8329,6 +8329,26 @@
         <attr name="successColor" format="color|reference"/>
     </declare-styleable>
 
+    <!-- =============================== -->
+    <!-- QuickAccessWallet attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Use <code>quickaccesswallet-service</code> as the root tag of the XML resource
+         that describes a {@link android.service.quickaccesswallet.QuickAccessWalletService},
+         which is referenced from its
+         {@link android.service.quickaccesswallet.QuickAccessWalletService#SERVICE_META_DATA}
+         meta-data entry.
+    -->
+    <declare-styleable name="QuickAccessWalletService">
+        <!-- Fully qualified class name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity"/>
+        <!-- Fully qualified class name of an activity that allows the user to view
+             their entire wallet -->
+        <attr name="targetActivity"/>
+    </declare-styleable>
+
     <!-- Use <code>recognition-service</code> as the root tag of the XML resource that
          describes a {@link android.speech.RecognitionService}, which is referenced from
          its {@link android.speech.RecognitionService#SERVICE_META_DATA} meta-data entry.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0bcadce..6aeb0a1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -174,6 +174,9 @@
     <!-- Adding Quick Settings tiles -->
     <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
 
+    <!-- Access Quick Access Wallet cards -->
+    <uses-permission android:name="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE" />
+
     <!-- Adding Controls to SystemUI -->
     <uses-permission android:name="android.permission.BIND_CONTROLS" />