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

Commit 7bf3913d authored by Charles Wang's avatar Charles Wang Committed by Android (Google) Code Review
Browse files

Merge "Support launching wallet on double tap." into main

parents ce8b0ba3 b193d904
Loading
Loading
Loading
Loading
+206 −24
Original line number Diff line number Diff line
@@ -16,9 +16,13 @@

package com.android.server;

import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;

import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;

import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +37,7 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -41,10 +46,12 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
@@ -70,7 +77,7 @@ public class GestureLauncherService extends SystemService {
     * Time in milliseconds in which the power button must be pressed twice so it will be considered
     * as a camera launch.
     */
    @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
    @VisibleForTesting static final long POWER_DOUBLE_TAP_MAX_TIME_MS = 300;


    /**
@@ -100,10 +107,23 @@ public class GestureLauncherService extends SystemService {
    @VisibleForTesting
    static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;

    /**
     * Number of taps required to launch camera shortcut.
     */
    private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
    /** Indicates camera should be launched on power double tap. */
    @VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0;

    /** Indicates wallet should be launched on power double tap. */
    @VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1;

    /** Number of taps required to launch the double tap shortcut (either camera or wallet). */
    public static final int DOUBLE_POWER_TAP_COUNT_THRESHOLD = 2;

    /** Bundle to send with PendingIntent to grant background activity start privileges. */
    private static final Bundle GRANT_BACKGROUND_START_PRIVILEGES =
            ActivityOptions.makeBasic()
                    .setPendingIntentBackgroundActivityStartMode(
                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS)
                    .toBundle();

    private final QuickAccessWalletClient mQuickAccessWalletClient;

    /** The listener that receives the gesture event. */
    private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -158,6 +178,9 @@ public class GestureLauncherService extends SystemService {
     */
    private boolean mCameraDoubleTapPowerEnabled;

    /** Whether wallet double tap power button gesture is currently enabled. */
    private boolean mWalletDoubleTapPowerEnabled;

    /**
     * Whether emergency gesture is currently enabled
     */
@@ -204,15 +227,17 @@ public class GestureLauncherService extends SystemService {
        }
    }
    public GestureLauncherService(Context context) {
        this(context, new MetricsLogger(), new UiEventLoggerImpl());
        this(context, new MetricsLogger(),
                QuickAccessWalletClient.create(context), new UiEventLoggerImpl());
    }

    @VisibleForTesting
    GestureLauncherService(Context context, MetricsLogger metricsLogger,
            UiEventLogger uiEventLogger) {
            QuickAccessWalletClient quickAccessWalletClient, UiEventLogger uiEventLogger) {
        super(context);
        mContext = context;
        mMetricsLogger = metricsLogger;
        mQuickAccessWalletClient = quickAccessWalletClient;
        mUiEventLogger = uiEventLogger;
    }

@@ -237,6 +262,9 @@ public class GestureLauncherService extends SystemService {
                    "GestureLauncherService");
            updateCameraRegistered();
            updateCameraDoubleTapPowerEnabled();
            if (launchWalletOptionOnPowerDoubleTap()) {
                updateWalletDoubleTapPowerEnabled();
            }
            updateEmergencyGestureEnabled();
            updateEmergencyGesturePowerButtonCooldownPeriodMs();

@@ -250,12 +278,24 @@ public class GestureLauncherService extends SystemService {
    }

    private void registerContentObservers() {
        if (launchWalletOptionOnPowerDoubleTap()) {
            mContext.getContentResolver().registerContentObserver(
                    Settings.Secure.getUriFor(
                            Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED),
                    false, mSettingObserver, mUserId);
            mContext.getContentResolver().registerContentObserver(
                    Settings.Secure.getUriFor(
                            Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE),
                    false, mSettingObserver, mUserId);
        } else {
            mContext.getContentResolver().registerContentObserver(
                    Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
                    false, mSettingObserver, mUserId);
            mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
                    Settings.Secure.getUriFor(
                            Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
                    false, mSettingObserver, mUserId);
        }
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
                false, mSettingObserver, mUserId);
@@ -291,6 +331,14 @@ public class GestureLauncherService extends SystemService {
        }
    }

    @VisibleForTesting
    void updateWalletDoubleTapPowerEnabled() {
        boolean enabled = isWalletDoubleTapPowerSettingEnabled(mContext, mUserId);
        synchronized (this) {
            mWalletDoubleTapPowerEnabled = enabled;
        }
    }

    @VisibleForTesting
    void updateEmergencyGestureEnabled() {
        boolean enabled = isEmergencyGestureSettingEnabled(mContext, mUserId);
@@ -418,11 +466,35 @@ public class GestureLauncherService extends SystemService {
                        Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
    }


    /** Checks if camera should be launched on double press of the power button. */
    public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
        return isCameraDoubleTapPowerEnabled(context.getResources())
        boolean res;

        if (launchWalletOptionOnPowerDoubleTap()) {
            res = isDoubleTapPowerGestureSettingEnabled(context, userId)
                    && getDoubleTapPowerGestureAction(context, userId)
                    == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
        } else {
            // These are legacy settings that will be deprecated once the option to launch both
            // wallet and camera has been created.
            res = isCameraDoubleTapPowerEnabled(context.getResources())
                    && (Settings.Secure.getIntForUser(context.getContentResolver(),
                    Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
        }
        return res;
    }

    /** Checks if wallet should be launched on double tap of the power button. */
    public static boolean isWalletDoubleTapPowerSettingEnabled(Context context, int userId) {
        if (!launchWalletOptionOnPowerDoubleTap()) {
            return false;
        }

        return isDoubleTapPowerGestureSettingEnabled(context, userId)
                && getDoubleTapPowerGestureAction(context, userId)
                == LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
    }

    public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) {
        return isCameraLiftTriggerEnabled(context.getResources())
@@ -441,6 +513,28 @@ public class GestureLauncherService extends SystemService {
                isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
    }

    private static int getDoubleTapPowerGestureAction(Context context, int userId) {
        return Settings.Secure.getIntForUser(
                context.getContentResolver(),
                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
                LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER,
                userId);
    }

    /** Whether the shortcut to launch app on power double press is enabled. */
    private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) {
        return Settings.Secure.getIntForUser(
                context.getContentResolver(),
                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
                isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0,
                userId)
                == 1;
    }

    private static boolean isDoubleTapConfigEnabled(Resources resources) {
        return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled);
    }

    /**
     * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
     * value is capped at a maximum
@@ -494,12 +588,19 @@ public class GestureLauncherService extends SystemService {
     * Whether GestureLauncherService should be enabled according to system properties.
     */
    public static boolean isGestureLauncherEnabled(Resources resources) {
        return isCameraLaunchEnabled(resources)
                || isCameraDoubleTapPowerEnabled(resources)
        boolean res =
                isCameraLaunchEnabled(resources)
                        || isCameraLiftTriggerEnabled(resources)
                        || isEmergencyGestureEnabled(resources);
        if (launchWalletOptionOnPowerDoubleTap()) {
            res |= isDoubleTapConfigEnabled(resources);
        } else {
            res |= isCameraDoubleTapPowerEnabled(resources);
        }
        return res;
    }


    /**
     * Attempts to intercept power key down event by detecting certain gesture patterns
     *
@@ -530,6 +631,7 @@ public class GestureLauncherService extends SystemService {
            return false;
        }
        boolean launchCamera = false;
        boolean launchWallet = false;
        boolean launchEmergencyGesture = false;
        boolean intercept = false;
        long powerTapInterval;
@@ -541,7 +643,7 @@ public class GestureLauncherService extends SystemService {
                mFirstPowerDown  = event.getEventTime();
                mPowerButtonConsecutiveTaps = 1;
                mPowerButtonSlowConsecutiveTaps = 1;
            } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
            } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) {
                // Tap too slow for shortcuts
                mFirstPowerDown  = event.getEventTime();
                mPowerButtonConsecutiveTaps = 1;
@@ -586,10 +688,16 @@ public class GestureLauncherService extends SystemService {
                }
            }
            if (mCameraDoubleTapPowerEnabled
                    && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
                    && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
                    && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
                    && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
                launchCamera = true;
                intercept = interactive;
            } else if (launchWalletOptionOnPowerDoubleTap()
                    && mWalletDoubleTapPowerEnabled
                    && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
                    && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
                launchWallet = true;
                intercept = interactive;
            }
        }
        if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
@@ -608,6 +716,10 @@ public class GestureLauncherService extends SystemService {
                        (int) powerTapInterval);
                mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
            }
        } else if (launchWallet) {
            Slog.i(TAG, "Power button double tap gesture detected, launching wallet. Interval="
                    + powerTapInterval + "ms");
            launchWallet = sendGestureTargetActivityPendingIntent();
        } else if (launchEmergencyGesture) {
            Slog.i(TAG, "Emergency gesture detected, launching.");
            launchEmergencyGesture = handleEmergencyGesture();
@@ -623,11 +735,75 @@ public class GestureLauncherService extends SystemService {
                mPowerButtonSlowConsecutiveTaps);
        mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);

        outLaunched.value = launchCamera || launchEmergencyGesture;
        outLaunched.value = launchCamera || launchEmergencyGesture || launchWallet;
        // Intercept power key event if the press is part of a gesture (camera, eGesture) and the
        // user has completed setup.
        return intercept && isUserSetupComplete();
    }

    /**
     * Fetches and sends gestureTargetActivityPendingIntent from QuickAccessWallet, which is a
     * specific activity that QuickAccessWalletService has defined to be launch on detection of the
     * power button gesture.
     */
    private boolean sendGestureTargetActivityPendingIntent() {
        boolean userSetupComplete = isUserSetupComplete();
        if (mQuickAccessWalletClient == null
                || !mQuickAccessWalletClient.isWalletServiceAvailable()) {
            Slog.w(TAG, "QuickAccessWalletService is not available, ignoring wallet gesture.");
            return false;
        }

        if (!userSetupComplete) {
            if (DBG) {
                Slog.d(TAG, "userSetupComplete = false, ignoring wallet gesture.");
            }
            return false;
        }
        if (DBG) {
            Slog.d(TAG, "userSetupComplete = true, performing wallet gesture.");
        }

        mQuickAccessWalletClient.getGestureTargetActivityPendingIntent(
                getContext().getMainExecutor(),
                gesturePendingIntent -> {
                    if (gesturePendingIntent == null) {
                        Slog.d(TAG, "getGestureTargetActivityPendingIntent is null.");
                        sendFallbackPendingIntent();
                        return;
                    }
                    sendPendingIntentWithBackgroundStartPrivileges(gesturePendingIntent);
                });
        return true;
    }

    /**
     * If gestureTargetActivityPendingIntent is null, this method is invoked to start the activity
     * that QuickAccessWalletService has defined to host the Wallet view, which is typically the
     * home screen of the Wallet application.
     */
    private void sendFallbackPendingIntent() {
        mQuickAccessWalletClient.getWalletPendingIntent(
                getContext().getMainExecutor(),
                walletPendingIntent -> {
                    if (walletPendingIntent == null) {
                        Slog.w(TAG, "getWalletPendingIntent returns null. Not launching "
                                + "anything for wallet.");
                        return;
                    }
                    sendPendingIntentWithBackgroundStartPrivileges(walletPendingIntent);
                });
    }

    private void sendPendingIntentWithBackgroundStartPrivileges(PendingIntent pendingIntent) {
        try {
            pendingIntent.send(GRANT_BACKGROUND_START_PRIVILEGES);
        } catch (PendingIntent.CanceledException e) {
            Slog.e(TAG, "PendingIntent was canceled", e);
        }
    }


    /**
     * @return true if camera was launched, false otherwise.
     */
@@ -709,6 +885,9 @@ public class GestureLauncherService extends SystemService {
                registerContentObservers();
                updateCameraRegistered();
                updateCameraDoubleTapPowerEnabled();
                if (launchWalletOptionOnPowerDoubleTap()) {
                    updateWalletDoubleTapPowerEnabled();
                }
                updateEmergencyGestureEnabled();
                updateEmergencyGesturePowerButtonCooldownPeriodMs();
            }
@@ -720,6 +899,9 @@ public class GestureLauncherService extends SystemService {
            if (userId == mUserId) {
                updateCameraRegistered();
                updateCameraDoubleTapPowerEnabled();
                if (launchWalletOptionOnPowerDoubleTap()) {
                    updateWalletDoubleTapPowerEnabled();
                }
                updateEmergencyGestureEnabled();
                updateEmergencyGesturePowerButtonCooldownPeriodMs();
            }
+463 −86

File changed.

Preview size limit exceeded, changes collapsed.