Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c402284 authored by Sean Pont's avatar Sean Pont Committed by Android (Google) Code Review
Browse files

Merge "[Quick Access Wallet] Update docs, remove debug logs, and fix lint errors" into rvc-dev

parents 61d30d7d 9d4fb036
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -3435,13 +3435,15 @@ package android.service.notification {

package android.service.quickaccesswallet {

  public interface QuickAccessWalletClient {
  public interface QuickAccessWalletClient extends java.io.Closeable {
    method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
    method public void addWalletServiceEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
    method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
    method @Nullable public android.content.Intent createWalletIntent();
    method @Nullable public android.content.Intent createWalletSettingsIntent();
    method public void disconnect();
    method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
    method public void getWalletCards(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
    method public boolean isWalletFeatureAvailable();
    method public boolean isWalletFeatureAvailableWhenDeviceLocked();
    method public boolean isWalletServiceAvailable();
+43 −24
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@ import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

import java.util.List;

/**
 * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
 *
@@ -56,7 +54,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
     *                 presented as the selected card.
     */
    public void onSuccess(@NonNull GetWalletCardsResponse response) {
        Log.i(TAG, "onSuccess");
        if (isValidResponse(response)) {
            mHandler.post(() -> onSuccessInternal(response));
        } else {
@@ -78,7 +75,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
    }

    private void onSuccessInternal(GetWalletCardsResponse response) {
        Log.i(TAG, "onSuccessInternal");
        if (mCalled) {
            Log.w(TAG, "already called");
            return;
@@ -86,7 +82,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
        mCalled = true;
        try {
            mCallback.onGetWalletCardsSuccess(response);
            Log.i(TAG, "onSuccessInternal: returned response");
        } catch (RemoteException e) {
            Log.w(TAG, "Error returning wallet cards", e);
        }
@@ -106,29 +101,53 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
    }

    private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
        return response != null
                && response.getWalletCards() != null
                && response.getSelectedIndex() >= 0
                && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
                || response.getSelectedIndex() < response.getWalletCards().size())
                && response.getWalletCards().size() < mRequest.getMaxCards()
                && areValidCards(response.getWalletCards());
        if (response == null) {
            Log.w(TAG, "Invalid response: response is null");
            return false;
        }

    private boolean areValidCards(List<WalletCard> walletCards) {
        for (WalletCard walletCard : walletCards) {
            if (walletCard == null
                    || walletCard.getCardId() == null
                    || walletCard.getCardImage() == null
                    || TextUtils.isEmpty(walletCard.getContentDescription())
                    || walletCard.getPendingIntent() == null) {
        if (response.getWalletCards() == null) {
            Log.w(TAG, "Invalid response: walletCards is null");
            return false;
        }
        if (response.getSelectedIndex() < 0) {
            Log.w(TAG, "Invalid response: selectedIndex is negative");
            return false;
        }
        if (!response.getWalletCards().isEmpty()
                && response.getSelectedIndex() >= response.getWalletCards().size()) {
            Log.w(TAG, "Invalid response: selectedIndex out of bounds");
            return false;
        }
        if (response.getWalletCards().size() > mRequest.getMaxCards()) {
            Log.w(TAG, "Invalid response: too many cards");
            return false;
        }
        for (WalletCard walletCard : response.getWalletCards()) {
            if (walletCard == null) {
                Log.w(TAG, "Invalid response: card is null");
                return false;
            }
            if (walletCard.getCardId() == null) {
                Log.w(TAG, "Invalid response: cardId is null");
                return false;
            }
            Icon cardImage = walletCard.getCardImage();
            if (cardImage == null) {
                Log.w(TAG, "Invalid response: cardImage is null");
                return false;
            }
            if (cardImage.getType() == Icon.TYPE_BITMAP
                    && walletCard.getCardImage().getBitmap().getConfig()
                    != Bitmap.Config.HARDWARE) {
                Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
                    && cardImage.getBitmap().getConfig() != Bitmap.Config.HARDWARE) {
                Log.w(TAG, "Invalid response: cardImage bitmaps must be hardware bitmaps");
                return false;
            }
            if (TextUtils.isEmpty(walletCard.getContentDescription())) {
                Log.w(TAG, "Invalid response: contentDescription is null");
                return false;
            }
            if (walletCard.getPendingIntent() == null) {
                Log.w(TAG, "Invalid response: pendingIntent is null");
                return false;
            }
        }
        return true;
+22 −3
Original line number Diff line number Diff line
@@ -16,19 +16,23 @@

package android.service.quickaccesswallet;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.content.Intent;

import java.io.Closeable;
import java.util.concurrent.Executor;

/**
 * Facilitates accessing cards from the {@link QuickAccessWalletService}.
 *
 * @hide
 */
@TestApi
public interface QuickAccessWalletClient {
public interface QuickAccessWalletClient extends Closeable {

    /**
     * Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the
@@ -91,6 +95,14 @@ public interface QuickAccessWalletClient {
            @NonNull GetWalletCardsRequest request,
            @NonNull OnWalletCardsRetrievedCallback callback);

    /**
     * Get wallet cards from the {@link QuickAccessWalletService}.
     */
    void getWalletCards(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull GetWalletCardsRequest request,
            @NonNull OnWalletCardsRetrievedCallback callback);

    /**
     * Callback for getWalletCards
     */
@@ -111,12 +123,19 @@ public interface QuickAccessWalletClient {
    void notifyWalletDismissed();

    /**
     * Unregister event listener.
     * Register an event listener.
     */
    void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);

    /**
     * Unregister event listener
     * Register an event listener.
     */
    void addWalletServiceEventListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull WalletServiceEventListener listener);

    /**
     * Unregister an event listener
     */
    void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);

+54 −52
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.service.quickaccesswallet;

import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -36,16 +37,19 @@ import android.util.Log;

import com.android.internal.widget.LockPatternUtils;

import java.io.IOException;
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.concurrent.Executor;

/**
 * Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
 * responses, and disconnects automatically after a short period of time. The client may
 * responses, and disconnects automatically one minute after the last call is performed.
 *
 * @hide
 */
public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
@@ -78,15 +82,14 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser

    @Override
    public boolean isWalletServiceAvailable() {
        boolean available = mServiceInfo != null;
        Log.i(TAG, "isWalletServiceAvailable: " + available);
        return available;
        return mServiceInfo != null;
    }

    @Override
    public boolean isWalletFeatureAvailable() {
        int currentUser = ActivityManager.getCurrentUser();
        return checkUserSetupComplete()
        return currentUser == UserHandle.USER_SYSTEM
                && checkUserSetupComplete()
                && checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
                && !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
    }
@@ -101,23 +104,29 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
    public void getWalletCards(
            @NonNull GetWalletCardsRequest request,
            @NonNull OnWalletCardsRetrievedCallback callback) {
        getWalletCards(mContext.getMainExecutor(), request, callback);
    }

        Log.i(TAG, "getWalletCards");

    @Override
    public void getWalletCards(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull GetWalletCardsRequest request,
            @NonNull OnWalletCardsRetrievedCallback callback) {
        if (!isWalletServiceAvailable()) {
            callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
            executor.execute(
                    () -> callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null)));
            return;
        }

        BaseCallbacks serviceCallback = new BaseCallbacks() {
            @Override
            public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
                mHandler.post(() -> callback.onWalletCardsRetrieved(response));
                executor.execute(() -> callback.onWalletCardsRetrieved(response));
            }

            @Override
            public void onGetWalletCardsFailure(GetWalletCardsError error) {
                mHandler.post(() -> callback.onWalletCardRetrievalError(error));
                executor.execute(() -> callback.onWalletCardRetrievalError(error));
            }
        };

@@ -132,11 +141,11 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
                serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
            }
        });

    }

    @Override
    public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
        Log.i(TAG, "selectWalletCard");
        if (!isWalletServiceAvailable()) {
            return;
        }
@@ -153,7 +162,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
        if (!isWalletServiceAvailable()) {
            return;
        }
        Log.i(TAG, "notifyWalletDismissed");
        executeApiCall(new ApiCaller("onWalletDismissed") {
            @Override
            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -164,15 +172,20 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser

    @Override
    public void addWalletServiceEventListener(WalletServiceEventListener listener) {
        addWalletServiceEventListener(mContext.getMainExecutor(), listener);
    }

    @Override
    public void addWalletServiceEventListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull WalletServiceEventListener listener) {
        if (!isWalletServiceAvailable()) {
            return;
        }
        Log.i(TAG, "registerWalletServiceEventListener");
        BaseCallbacks callback = new BaseCallbacks() {
            @Override
            public void onWalletServiceEvent(WalletServiceEvent event) {
                Log.i(TAG, "onWalletServiceEvent");
                mHandler.post(() -> listener.onWalletServiceEvent(event));
                executor.execute(() -> listener.onWalletServiceEvent(event));
            }
        };

@@ -193,7 +206,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
        if (!isWalletServiceAvailable()) {
            return;
        }
        Log.i(TAG, "unregisterWalletServiceEventListener");
        executeApiCall(new ApiCaller("unregisterListener") {
            @Override
            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -208,9 +220,13 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
        });
    }

    @Override
    public void close() throws IOException {
        disconnect();
    }

    @Override
    public void disconnect() {
        Log.i(TAG, "disconnect");
        mHandler.post(() -> disconnectInternal(true));
    }

@@ -241,18 +257,15 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
    }

    private void connect() {
        Log.i(TAG, "connect");
        mHandler.post(this::connectInternal);
    }

    private void connectInternal() {
        Log.i(TAG, "connectInternal");
        if (mServiceInfo == null) {
            Log.w(TAG, "Wallet service unavailable");
            return;
        }
        if (mIsConnected) {
            Log.w(TAG, "already connected");
            return;
        }
        mIsConnected = true;
@@ -264,23 +277,14 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
    }

    private void onConnectedInternal(IQuickAccessWalletService service) {
        Log.i(TAG, "onConnectedInternal");
        if (!mIsConnected) {
            Log.w(TAG, "onConnectInternal but connection closed");
            mService = null;
            return;
        }
        mService = service;
        Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
        for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
            try {
                apiCaller.performApiCall(mService);
            } catch (RemoteException e) {
                Log.e(TAG, "onConnectedInternal error", e);
                apiCaller.onApiError();
                disconnect();
                break;
            }
            performApiCallInternal(apiCaller, mService);
            mRequestQueue.remove(apiCaller);
        }
    }
@@ -290,7 +294,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
     * posting a new delayed message.
     */
    private void resetServiceConnectionTimeout() {
        Log.i(TAG, "resetServiceConnectionTimeout");
        mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
        mHandler.postDelayed(
                () -> disconnectInternal(true),
@@ -299,13 +302,11 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
    }

    private void disconnectInternal(boolean clearEventListeners) {
        Log.i(TAG, "disconnectInternal: " + clearEventListeners);
        if (!mIsConnected) {
            Log.w(TAG, "already disconnected");
            return;
        }
        if (clearEventListeners && !mEventListeners.isEmpty()) {
            Log.i(TAG, "disconnectInternal: clear event listeners");
            for (WalletServiceEventListener listener : mEventListeners.keySet()) {
                removeWalletServiceEventListener(listener);
            }
@@ -320,27 +321,31 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
    }

    private void executeApiCall(ApiCaller apiCaller) {
        Log.i(TAG, "execute: " + apiCaller.mDesc);
        mHandler.post(() -> executeInternal(apiCaller));
    }

    private void executeInternal(ApiCaller apiCall) {
        Log.i(TAG, "executeInternal: " + apiCall.mDesc);
    private void executeInternal(ApiCaller apiCaller) {
        if (mIsConnected && mService != null) {
            performApiCallInternal(apiCaller, mService);
        } else {
            mRequestQueue.add(apiCaller);
            connect();
        }
    }

    private void performApiCallInternal(ApiCaller apiCaller, IQuickAccessWalletService service) {
        if (service == null) {
            apiCaller.onApiError();
            return;
        }
        try {
                apiCall.performApiCall(mService);
                Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
            apiCaller.performApiCall(service);
            resetServiceConnectionTimeout();
        } catch (RemoteException e) {
                Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
                apiCall.onApiError();
            Log.w(TAG, "executeInternal error: " + apiCaller.mDesc, e);
            apiCaller.onApiError();
            disconnect();
        }
        } else {
            Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
            mRequestQueue.add(apiCall);
            connect();
        }
    }

    private abstract static class ApiCaller {
@@ -350,7 +355,8 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
            this.mDesc = desc;
        }

        abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
        abstract void performApiCall(IQuickAccessWalletService service)
                throws RemoteException;

        void onApiError() {
            Log.w(TAG, "api error: " + mDesc);
@@ -359,7 +365,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser

    @Override // ServiceConnection
    public void onServiceConnected(ComponentName name, IBinder binder) {
        Log.i(TAG, "onServiceConnected: " + name);
        IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
        mHandler.post(() -> onConnectedInternal(service));
    }
@@ -367,19 +372,16 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
    @Override // ServiceConnection
    public void onServiceDisconnected(ComponentName name) {
        // Do not disconnect, as we may later be re-connected
        Log.w(TAG, "onServiceDisconnected");
    }

    @Override // ServiceConnection
    public void onBindingDied(ComponentName name) {
        // This is a recoverable error but the client will need to reconnect.
        Log.w(TAG, "onBindingDied");
        disconnect();
    }

    @Override // ServiceConnection
    public void onNullBinding(ComponentName name) {
        Log.w(TAG, "onNullBinding");
        disconnect();
    }

+0 −2
Original line number Diff line number Diff line
@@ -66,13 +66,11 @@ class QuickAccessWalletServiceInfo {
    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;
        }

Loading