diff options
| author | 2022-07-26 20:56:14 +0000 | |
|---|---|---|
| committer | 2022-07-26 20:56:14 +0000 | |
| commit | 315f6f124d66af1983cc6813c21191a62b038f1d (patch) | |
| tree | 9953466626fb65a9b362cdaa4dadaa77699f2d0b | |
| parent | c7a745c7fa6272965f0a0144c2b9e10bdc781321 (diff) | |
| parent | 2ff2a64cfe225ca5d1d554cffae98b2b5f735f74 (diff) | |
Merge "SideFPS UX updates" into tm-qpr-dev am: 2ff2a64cfe
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19039844
Change-Id: I0e5645ff7a507ebd9c8c29f347ed6e45f3f365d3
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
29 files changed, 716 insertions, 154 deletions
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 99e4febe205d..943eee461809 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -157,6 +157,11 @@ public interface BiometricConstants { int BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED = 18; /** + * A power press stopped this biometric operation. + * @hide + */ + int BIOMETRIC_ERROR_POWER_PRESSED = 19; + /** * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused * because the authentication attempt was unsuccessful. * @hide diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index fd46f243874b..2b62b98529a9 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -70,6 +70,7 @@ public interface BiometricFaceConstants { BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, BIOMETRIC_ERROR_RE_ENROLL, FACE_ERROR_UNKNOWN, + BIOMETRIC_ERROR_POWER_PRESSED, }) @Retention(RetentionPolicy.SOURCE) @interface FaceError {} @@ -184,6 +185,12 @@ public interface BiometricFaceConstants { int FACE_ERROR_UNKNOWN = 17; /** + * A power press stopped this biometric operation. + * @hide + */ + int BIOMETRIC_ERROR_POWER_PRESSED = 19; + + /** * Vendor codes received from the HAL start at 0. Codes that the framework exposes to keyguard * append this value for some reason. We should probably remove this and just send the actual * vendor code. diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index d8ebb628452a..98f571b80949 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -61,7 +61,8 @@ public interface BiometricFingerprintConstants { BIOMETRIC_ERROR_RE_ENROLL, BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, FINGERPRINT_ERROR_UNKNOWN, - FINGERPRINT_ERROR_BAD_CALIBRATION}) + FINGERPRINT_ERROR_BAD_CALIBRATION, + BIOMETRIC_ERROR_POWER_PRESSED}) @Retention(RetentionPolicy.SOURCE) @interface FingerprintError {} @@ -188,6 +189,12 @@ public interface BiometricFingerprintConstants { int FINGERPRINT_ERROR_BAD_CALIBRATION = 18; /** + * A power press stopped this biometric operation. + * @hide + */ + int BIOMETRIC_ERROR_POWER_PRESSED = 19; + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) diff --git a/core/java/android/hardware/biometrics/BiometricStateListener.java b/core/java/android/hardware/biometrics/BiometricStateListener.java index 2ac0c1edaef0..b167cc6323d3 100644 --- a/core/java/android/hardware/biometrics/BiometricStateListener.java +++ b/core/java/android/hardware/biometrics/BiometricStateListener.java @@ -46,6 +46,14 @@ public abstract class BiometricStateListener extends IBiometricStateListener.Stu public @interface State { } + // The sensor received a touch. + public static final int ACTION_SENSOR_TOUCH = 0; + + @IntDef({ACTION_SENSOR_TOUCH}) + @Retention(RetentionPolicy.SOURCE) + public @interface Action { + } + /** * Defines behavior in response to state update * @param newState new state of the biometric sensor @@ -53,6 +61,13 @@ public abstract class BiometricStateListener extends IBiometricStateListener.Stu public void onStateChanged(@BiometricStateListener.State int newState) { } + + /** + * Invoked when a biometric action has occurred. + */ + public void onBiometricAction(@BiometricStateListener.Action int action) { + } + /** * Invoked when enrollment state changes for the specified user */ diff --git a/core/java/android/hardware/biometrics/IBiometricStateListener.aidl b/core/java/android/hardware/biometrics/IBiometricStateListener.aidl index 5bdced034017..6bb170d1cb62 100644 --- a/core/java/android/hardware/biometrics/IBiometricStateListener.aidl +++ b/core/java/android/hardware/biometrics/IBiometricStateListener.aidl @@ -22,5 +22,6 @@ package android.hardware.biometrics; */ oneway interface IBiometricStateListener { void onStateChanged(int newState); + void onBiometricAction(int action); void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 28f1f02b7a93..c614cdbc3199 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -101,6 +101,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private static final int MSG_FINGERPRINT_DETECTED = 107; private static final int MSG_UDFPS_POINTER_DOWN = 108; private static final int MSG_UDFPS_POINTER_UP = 109; + private static final int MSG_POWER_BUTTON_PRESSED = 110; /** * @hide @@ -984,6 +985,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * This is triggered by SideFpsEventHandler + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void onPowerPressed() { + Slog.i(TAG, "onPowerPressed"); + mHandler.obtainMessage(MSG_POWER_BUTTON_PRESSED).sendToTarget(); + } + + /** * Determine if there is at least one fingerprint enrolled. * * @return true if at least one fingerprint is enrolled, false otherwise @@ -1196,6 +1207,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case MSG_UDFPS_POINTER_UP: sendUdfpsPointerUp(msg.arg1 /* sensorId */); break; + case MSG_POWER_BUTTON_PRESSED: + sendPowerPressed(); + break; default: Slog.w(TAG, "Unknown message: " + msg.what); @@ -1325,6 +1339,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mAuthenticationCallback.onUdfpsPointerUp(sensorId); } + private void sendPowerPressed() { + try { + mService.onPowerPressed(); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending power press", e); + } + } + /** * @hide */ @@ -1461,6 +1483,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case FINGERPRINT_ERROR_BAD_CALIBRATION: return context.getString( com.android.internal.R.string.fingerprint_error_bad_calibration); + case BIOMETRIC_ERROR_POWER_PRESSED: + return context.getString( + com.android.internal.R.string.fingerprint_error_power_pressed); case FINGERPRINT_ERROR_VENDOR: { String[] msgArray = context.getResources().getStringArray( com.android.internal.R.array.fingerprint_error_vendor); diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 0b6344617663..20cc58cc78a7 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -171,4 +171,7 @@ interface IFingerprintService { // Registers BiometricStateListener. void registerBiometricStateListener(IBiometricStateListener listener); + + // Sends a power button pressed event to all listeners. + oneway void onPowerPressed(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2167ff5202bf..7a5b4a21c0ad 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9704,6 +9704,43 @@ public final class Settings { public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll"; /** + * The time (in millis) to wait for a power button before sending a + * successful auth in to keyguard(for side fingerprint) + * @hide + */ + @Readable + public static final String FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW = + "fingerprint_side_fps_kg_power_window"; + + /** + * The time (in millis) to wait for a power button before sending + * a successful auth in biometric prompt(for side fingerprint) + * @hide + */ + @Readable + public static final String FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW = + "fingerprint_side_fps_bp_power_window"; + + /** + * The time (in millis) that a finger tap will wait for a power button + * before dismissing the power dialog during enrollment(for side + * fingerprint) + * @hide + */ + @Readable + public static final String FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW = + "fingerprint_side_fps_enroll_tap_window"; + + /** + * The time (in millis) that a power event will ignore future authentications + * (for side fingerprint) + * @hide + */ + @Readable + public static final String FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME = + "fingerprint_side_fps_auth_downtime"; + + /** * Whether or not debugging is enabled. * @hide */ diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bafaf5a98ac6..190d7bddfa17 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3455,6 +3455,22 @@ <!-- Specify if the fingerprint hardware support gestures--> <bool name="config_fingerprintSupportsGestures">false</bool> + <!-- The time (in millis) to wait for a power button before sending + a successful auth in biometric prompt(for side fingerprint) --> + <integer name="config_sidefpsBpPowerPressWindow">300</integer> + + <!-- The time (in millis) to wait for a power button before sending a + successful auth in to keyguard(for side fingerprint) --> + <integer name="config_sidefpsKeyguardPowerPressWindow">300</integer> + + <!-- The time (in millis) that a power event will ignore future authentications + (for side fingerprint) --> + <integer name="config_sidefpsPostAuthDowntime">400</integer> + + <!-- The time (in millis) that a finger tap will wait for a power button + before dismissing the power dialog during enrollment(for side fingerprint) --> + <integer name="config_sidefpsEnrollPowerPressWindow">300</integer> + <!-- This config is used to force VoiceInteractionService to start on certain low ram devices. It declares the package name of VoiceInteractionService that should be started. --> <string translatable="false" name="config_forceVoiceInteractionServicePackage"></string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1f00dc241973..26eee0db8aa1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1739,6 +1739,8 @@ <string name="fingerprint_error_security_update_required">Sensor temporarily disabled.</string> <!-- Generic error message shown when fingerprint needs calibration [CHAR LIMIT=150] --> <string name="fingerprint_error_bad_calibration">Can\u2019t use fingerprint sensor. Visit a repair provider</string> + <!-- Generic error message shown when the power button has been pressed. [CHAR LIMIT=150] --> + <string name="fingerprint_error_power_pressed">Power button pressed</string> <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5b919c4ac2c2..74ef49d3b901 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2611,10 +2611,15 @@ <java-symbol type="string" name="fingerprint_recalibrate_notification_name" /> <java-symbol type="string" name="fingerprint_recalibrate_notification_title" /> <java-symbol type="string" name="fingerprint_recalibrate_notification_content" /> + <java-symbol type="string" name="fingerprint_error_power_pressed" /> <!-- Fingerprint config --> <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/> <java-symbol type="bool" name="config_fingerprintSupportsGestures"/> + <java-symbol type="integer" name="config_sidefpsBpPowerPressWindow"/> + <java-symbol type="integer" name="config_sidefpsKeyguardPowerPressWindow"/> + <java-symbol type="integer" name="config_sidefpsPostAuthDowntime"/> + <java-symbol type="integer" name="config_sidefpsEnrollPowerPressWindow"/> <!-- Face authentication messages --> <java-symbol type="string" name="face_recalibrate_notification_name" /> diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index ce331607c531..d1f10a640dbf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -118,6 +118,10 @@ public class SecureSettings { Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, Settings.Secure.FACE_UNLOCK_APP_ENABLED, Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, + Settings.Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW, + Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW, + Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW, + Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, Settings.Secure.ACTIVE_UNLOCK_ON_WAKE, Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 5d773789b206..4aadf72930aa 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -172,6 +172,11 @@ public class SecureSettingsValidators { Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.FACE_UNLOCK_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW, + NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index c19175742ce5..4ae2cad4a01f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -805,8 +805,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private Runnable mRetryFingerprintAuthentication = new Runnable() { @Override public void run() { - Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " + - mHardwareFingerprintUnavailableRetryCount); + Log.w(TAG, + "Retrying fingerprint attempt: " + mHardwareFingerprintUnavailableRetryCount); if (mFpm.isHardwareDetected()) { updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) { @@ -833,7 +833,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab setFingerprintRunningState(BIOMETRIC_STATE_STOPPED); } - if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) { + if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE + || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED) { + Log.d(TAG, "Fingerprint retrying auth due to(" + msgId + ") -> " + errString); mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java index 1b24aa896652..0d789f7a1840 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java @@ -108,6 +108,17 @@ public class BiometricStateCallback implements ClientMonitorCallback { } } + @Override + public void onBiometricAction(@BiometricStateListener.Action int action) { + for (IBiometricStateListener listener : mBiometricStateListeners) { + try { + listener.onBiometricAction(action); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onBiometricAction", e); + } + } + } + /** * This should be invoked when: * 1) Enrolled --> None-enrolled diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java index 8ea4ee911cb1..4417f9270d9c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java @@ -31,6 +31,11 @@ public interface ClientMonitorCallback { default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {} /** + * Invoked when a biometric action has occurred. + */ + default void onBiometricAction(int action) {} + + /** * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge, * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java index b82f5fa129d6..07041bf8ce3d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java @@ -44,6 +44,13 @@ public class ClientMonitorCompositeCallback implements ClientMonitorCallback { } @Override + public final void onBiometricAction(int action) { + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onBiometricAction(action); + } + } + + @Override public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { for (int i = mCallbacks.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 0c5b19b0fb2e..94b67cedf86c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -1020,9 +1020,18 @@ public class FingerprintService extends SystemService { @Override public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); FingerprintService.this.registerBiometricStateListener(listener); } - } + + @Override + public void onPowerPressed() { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + for (ServiceProvider provider : mServiceProviders) { + provider.onPowerPressed(); + } + } + }; public FingerprintService(Context context) { super(context); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java new file mode 100644 index 000000000000..288c372a3f13 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 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 com.android.server.biometrics.sensors.fingerprint; + +/** + * Interface for handling power presses. + */ +public interface PowerPressHandler { + /** + * Indicates a power press has occurred. + */ + void onPowerPressed(); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 9cdbdc9158fb..24a47e081498 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -150,6 +150,8 @@ public interface ServiceProvider { void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller); + void onPowerPressed(); + /** * Sets side-fps controller * @param controller side-fps controller diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 79e3bf53acd3..e1626f014d8b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.app.TaskStackListener; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; import android.hardware.biometrics.common.ICancellationSignal; @@ -29,10 +30,15 @@ import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.Build; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; import android.util.Slog; +import com.android.internal.R; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; @@ -46,17 +52,18 @@ import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.SensorOverlays; +import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler; import com.android.server.biometrics.sensors.fingerprint.Udfps; import java.util.ArrayList; import java.util.function.Supplier; /** - * Fingerprint-specific authentication client supporting the - * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface. + * Fingerprint-specific authentication client supporting the {@link + * android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface. */ -class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> implements - Udfps, LockoutConsumer { +class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> + implements Udfps, LockoutConsumer, PowerPressHandler { private static final String TAG = "FingerprintAuthenticationClient"; @NonNull private final LockoutCache mLockoutCache; @@ -64,33 +71,88 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull private final FingerprintSensorPropertiesInternal mSensorProps; @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback; - @Nullable private ICancellationSignal mCancellationSignal; + @Nullable + private ICancellationSignal mCancellationSignal; private boolean mIsPointerDown; + private final Handler mHandler; - FingerprintAuthenticationClient(@NonNull Context context, + private static final int MESSAGE_IGNORE_AUTH = 1; + private static final int MESSAGE_AUTH_SUCCESS = 2; + private long mWaitForAuthKeyguard; + private long mWaitForAuthBp; + private long mIgnoreAuthFor; + + FingerprintAuthenticationClient( + @NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, - @NonNull IBinder token, long requestId, - @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, - boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, + @NonNull IBinder token, + long requestId, + @NonNull ClientMonitorCallbackConverter listener, + int targetUserId, + long operationId, + boolean restricted, + @NonNull String owner, + int cookie, + boolean requireConfirmation, int sensorId, - @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + @NonNull BiometricLogger biometricLogger, + @NonNull BiometricContext biometricContext, boolean isStrongBiometric, - @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache, + @Nullable TaskStackListener taskStackListener, + @NonNull LockoutCache lockoutCache, @Nullable IUdfpsOverlayController udfpsOverlayController, @Nullable ISidefpsController sidefpsController, boolean allowBackgroundAuthentication, - @NonNull FingerprintSensorPropertiesInternal sensorProps) { - super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, - cookie, requireConfirmation, sensorId, - biometricLogger, biometricContext, - isStrongBiometric, taskStackListener, - lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */, + @NonNull FingerprintSensorPropertiesInternal sensorProps, + @NonNull Handler handler) { + super( + context, + lazyDaemon, + token, + listener, + targetUserId, + operationId, + restricted, + owner, + cookie, + requireConfirmation, + sensorId, + biometricLogger, + biometricContext, + isStrongBiometric, + taskStackListener, + lockoutCache, + allowBackgroundAuthentication, + true /* shouldVibrate */, false /* isKeyguardBypassEnabled */); setRequestId(requestId); mLockoutCache = lockoutCache; mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); mSensorProps = sensorProps; mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */); + mHandler = handler; + + mWaitForAuthKeyguard = + context.getResources() + .getInteger(R.integer.config_sidefpsKeyguardPowerPressWindow); + mWaitForAuthBp = + context.getResources().getInteger(R.integer.config_sidefpsBpPowerPressWindow); + mIgnoreAuthFor = + context.getResources().getInteger(R.integer.config_sidefpsPostAuthDowntime); + + if (mSensorProps.isAnySidefpsType()) { + if (Build.isDebuggable()) { + mWaitForAuthKeyguard = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW, + (int) mWaitForAuthKeyguard, UserHandle.USER_CURRENT); + mWaitForAuthBp = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW, (int) mWaitForAuthBp, + UserHandle.USER_CURRENT); + mIgnoreAuthFor = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, (int) mIgnoreAuthFor, + UserHandle.USER_CURRENT); + } + } } @Override @@ -108,8 +170,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull @Override protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { - return new ClientMonitorCompositeCallback(mALSProbeCallback, - getBiometricContextUnsubscriber(), callback); + return new ClientMonitorCompositeCallback( + mALSProbeCallback, getBiometricContextUnsubscriber(), callback); } @Override @@ -126,16 +188,37 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> } @Override - public void onAuthenticated(BiometricAuthenticator.Identifier identifier, - boolean authenticated, ArrayList<Byte> token) { - super.onAuthenticated(identifier, authenticated, token); - - if (authenticated) { - mState = STATE_STOPPED; - mSensorOverlays.hide(getSensorId()); - } else { - mState = STATE_STARTED_PAUSED_ATTEMPTED; + public void onAuthenticated( + BiometricAuthenticator.Identifier identifier, + boolean authenticated, + ArrayList<Byte> token) { + + long delay = 0; + if (authenticated && mSensorProps.isAnySidefpsType()) { + if (mHandler.hasMessages(MESSAGE_IGNORE_AUTH)) { + Slog.i(TAG, "(sideFPS) Ignoring auth due to recent power press"); + onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true); + return; + } + delay = isKeyguard() ? mWaitForAuthKeyguard : mWaitForAuthBp; + Slog.i(TAG, "(sideFPS) Auth succeeded, sideFps waiting for power until: " + delay); } + + mHandler.postDelayed( + () -> { + if (authenticated && mSensorProps.isAnySidefpsType()) { + Slog.i(TAG, "(sideFPS): No power press detected, sending auth"); + } + super.onAuthenticated(identifier, authenticated, token); + if (authenticated) { + mState = STATE_STOPPED; + mSensorOverlays.hide(getSensorId()); + } else { + mState = STATE_STARTED_PAUSED_ATTEMPTED; + } + }, + MESSAGE_AUTH_SUCCESS, + delay); } @Override @@ -165,7 +248,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mCancellationSignal = doAuthenticate(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); - onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + onError( + BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); mSensorOverlays.hide(getSensorId()); mCallback.onClientFinished(this, false /* success */); @@ -177,15 +261,18 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> if (session.hasContextMethods()) { final OperationContext opContext = getOperationContext(); - final ICancellationSignal cancel = session.getSession().authenticateWithContext( - mOperationId, opContext); - getBiometricContext().subscribe(opContext, ctx -> { - try { - session.getSession().onContextChanged(ctx); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to notify context changed", e); - } - }); + final ICancellationSignal cancel = + session.getSession().authenticateWithContext(mOperationId, opContext); + getBiometricContext() + .subscribe( + opContext, + ctx -> { + try { + session.getSession().onContextChanged(ctx); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify context changed", e); + } + }); return cancel; } else { return session.getSession().authenticate(mOperationId); @@ -202,7 +289,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mCancellationSignal.cancel(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); - onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + onError( + BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); mCallback.onClientFinished(this, false /* success */); } @@ -284,8 +372,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); // Lockout metrics are logged as an error code. final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; - getLogger().logOnError(getContext(), getOperationContext(), - error, 0 /* vendorCode */, getTargetUserId()); + getLogger() + .logOnError( + getContext(), + getOperationContext(), + error, + 0 /* vendorCode */, + getTargetUserId()); try { getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); @@ -303,8 +396,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); // Lockout metrics are logged as an error code. final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; - getLogger().logOnError(getContext(), getOperationContext(), - error, 0 /* vendorCode */, getTargetUserId()); + getLogger() + .logOnError( + getContext(), + getOperationContext(), + error, + 0 /* vendorCode */, + getTargetUserId()); try { getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); @@ -315,4 +413,19 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mSensorOverlays.hide(getSensorId()); mCallback.onClientFinished(this, false /* success */); } + + @Override + public void onPowerPressed() { + if (mSensorProps.isAnySidefpsType()) { + Slog.i(TAG, "(sideFPS): onPowerPressed"); + if (mHandler.hasMessages(MESSAGE_AUTH_SUCCESS)) { + Slog.i(TAG, "(sideFPS): Ignoring auth in queue"); + mHandler.removeMessages(MESSAGE_AUTH_SUCCESS); + // Do not call onError() as that will send an additional callback to coex. + onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true); + } + mHandler.removeMessages(MESSAGE_IGNORE_AUTH); + mHandler.postDelayed(() -> {}, MESSAGE_IGNORE_AUTH, mIgnoreAuthFor); + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index f23659c94e49..f4f0a19d0db7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -22,6 +22,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; +import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.PointerContext; @@ -139,7 +140,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps controller.onEnrollmentHelp(getSensorId()); } }); - + mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH); super.onAcquired(acquiredInfo, vendorCode); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index f16af78364a2..6f6c09b91a66 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -67,6 +67,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; +import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler; import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; import com.android.server.biometrics.sensors.fingerprint.Udfps; @@ -388,6 +389,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override + public void onBiometricAction(int action) { + mBiometricStateCallback.onBiometricAction(action); + } + + @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mBiometricStateCallback.onClientFinished(clientMonitor, success); @@ -441,7 +447,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mBiometricContext, isStrongBiometric, mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, - mSensors.get(sensorId).getSensorProperties()); + mSensors.get(sensorId).getSensorProperties(), mHandler); scheduleForSensor(sensorId, client, mBiometricStateCallback); }); } @@ -614,6 +620,21 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override + public void onPowerPressed() { + for (int i = 0; i < mSensors.size(); i++) { + final Sensor sensor = mSensors.valueAt(i); + BaseClientMonitor client = sensor.getScheduler().getCurrentClient(); + if (client == null) { + return; + } + if (!(client instanceof PowerPressHandler)) { + continue; + } + ((PowerPressHandler) client).onPowerPressed(); + } + } + + @Override public void setSidefpsController(@NonNull ISidefpsController controller) { mSidefpsController = controller; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 2a3f34ae3cd4..c1a86386dfd4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -603,6 +603,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override + public void onBiometricAction(int action) { + mBiometricStateCallback.onBiometricAction(action); + } + + @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mBiometricStateCallback.onClientFinished(clientMonitor, success); @@ -821,6 +826,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override + public void onPowerPressed() { + Slog.e(TAG, "onPowerPressed not supported for HIDL clients"); + } + + @Override public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) { mUdfpsOverlayController = controller; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 1d478e53fd38..2a59c8c0f204 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; @@ -151,6 +152,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint controller.onEnrollmentHelp(getSensorId()); } }); + + mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH); } @Override diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index d88949bcc0ec..d645bb211bc3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -949,6 +949,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) { + // SideFPS still needs to know about suppressed power buttons, in case it needs to block + // an auth attempt. + if (count == 1) { + mSideFpsEventHandler.notifyPowerPressed(); + } if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { Slog.i(TAG, "Suppressed redundant power key press while " + "already in the process of turning the screen on."); @@ -968,7 +973,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (count > 3 && count <= getMaxMultiPressPowerCount()) { Slog.d(TAG, "No behavior defined for power press count " + count); } else if (count == 1 && interactive && !beganFromNonInteractive) { - if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) { + if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) { Slog.i(TAG, "Suppressing power key because the user is interacting with the " + "fingerprint sensor"); return; diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java index 41d02727d9eb..af2b902c97e8 100644 --- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java +++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java @@ -19,6 +19,7 @@ package com.android.server.policy; import static android.hardware.biometrics.BiometricStateListener.STATE_BP_AUTH; import static android.hardware.biometrics.BiometricStateListener.STATE_ENROLLING; import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE; +import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,8 +35,12 @@ import android.hardware.biometrics.BiometricStateListener; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.os.Build; import android.os.Handler; import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; import android.view.WindowManager; import com.android.internal.R; @@ -46,14 +51,26 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; /** - * Defines behavior for handling interactions between power button events and - * fingerprint-related operations, for devices where the fingerprint sensor (side fps) - * lives on the power button. + * Defines behavior for handling interactions between power button events and fingerprint-related + * operations, for devices where the fingerprint sensor (side fps) lives on the power button. */ public class SideFpsEventHandler { private static final int DEBOUNCE_DELAY_MILLIS = 500; + private int getTapWaitForPowerDuration(Context context) { + int tap = context.getResources().getInteger( + R.integer.config_sidefpsEnrollPowerPressWindow); + if (Build.isDebuggable()) { + tap = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW, tap, + UserHandle.USER_CURRENT); + } + return tap; + } + + private static final String TAG = "SideFpsEventHandler"; + @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final PowerManager mPowerManager; @@ -61,20 +78,26 @@ public class SideFpsEventHandler { @NonNull private final AtomicBoolean mSideFpsEventHandlerReady; @Nullable private Dialog mDialog; - @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener = (dialog) -> { - if (mDialog == dialog) { - mDialog = null; - } - }; + private final Runnable mTurnOffDialog = + () -> { + dismissDialog("mTurnOffDialog"); + }; + + @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener; private @BiometricStateListener.State int mBiometricState; + private final int mTapWaitForPowerDuration; + private FingerprintManager mFingerprintManager; SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager) { this(context, handler, powerManager, () -> new AlertDialog.Builder(context)); } @VisibleForTesting - SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager, + SideFpsEventHandler( + Context context, + Handler handler, + PowerManager powerManager, Supplier<AlertDialog.Builder> dialogSupplier) { mContext = context; mHandler = handler; @@ -82,91 +105,131 @@ public class SideFpsEventHandler { mDialogSupplier = dialogSupplier; mBiometricState = STATE_IDLE; mSideFpsEventHandlerReady = new AtomicBoolean(false); + mDialogDismissListener = + (dialog) -> { + if (mDialog == dialog) { + if (mHandler != null) { + mHandler.removeCallbacks(mTurnOffDialog); + } + mDialog = null; + } + }; // ensure dialog is dismissed if screen goes off for unrelated reasons - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (mDialog != null) { - mDialog.dismiss(); - mDialog = null; - } - } - }, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + context.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + } + }, + new IntentFilter(Intent.ACTION_SCREEN_OFF)); + mTapWaitForPowerDuration = getTapWaitForPowerDuration(context); } /** - * Called from {@link PhoneWindowManager} after the power button is pressed and displays a - * dialog confirming the user's intent to turn screen off if a fingerprint operation is - * active. The device goes to sleep if confirmed otherwise the dialog is dismissed. + * Called from {@link PhoneWindowManager} to notify FingerprintManager that a single tap power + * button has been pressed. + */ + public void notifyPowerPressed() { + Log.i(TAG, "notifyPowerPressed"); + if (mFingerprintManager == null) { + mFingerprintManager = mContext.getSystemService(FingerprintManager.class); + } + if (mFingerprintManager == null) { + return; + } + mFingerprintManager.onPowerPressed(); + } + + /** + * Called from {@link PhoneWindowManager} and will dictate if the SideFpsEventHandler should + * handle the power press. * * @param eventTime powerPress event time * @return true if powerPress was consumed, false otherwise */ - public boolean onSinglePressDetected(long eventTime) { + public boolean shouldConsumeSinglePress(long eventTime) { if (!mSideFpsEventHandlerReady.get()) { return false; } switch (mBiometricState) { case STATE_ENROLLING: + mHandler.post( + () -> { + if (mHandler.hasCallbacks(mTurnOffDialog)) { + Log.v(TAG, "Detected a tap to turn off dialog, ignoring"); + mHandler.removeCallbacks(mTurnOffDialog); + } + }); + showDialog(eventTime, "Enroll Power Press"); + return true; case STATE_BP_AUTH: - mHandler.post(() -> { - if (mDialog != null) { - mDialog.dismiss(); - } - mDialog = showConfirmDialog(mDialogSupplier.get(), - mPowerManager, eventTime, mBiometricState, mDialogDismissListener); - }); return true; + case STATE_KEYGUARD_AUTH: default: return false; } } @NonNull - private static Dialog showConfirmDialog(@NonNull AlertDialog.Builder dialogBuilder, - @NonNull PowerManager powerManager, long eventTime, + private static Dialog showConfirmDialog( + @NonNull AlertDialog.Builder dialogBuilder, + @NonNull PowerManager powerManager, + long eventTime, @BiometricStateListener.State int biometricState, @NonNull DialogInterface.OnDismissListener dismissListener) { final boolean enrolling = biometricState == STATE_ENROLLING; - final int title = enrolling ? R.string.fp_power_button_enrollment_title - : R.string.fp_power_button_bp_title; - final int message = enrolling ? R.string.fp_power_button_enrollment_message - : R.string.fp_power_button_bp_message; - final int positiveText = enrolling ? R.string.fp_power_button_enrollment_positive_button - : R.string.fp_power_button_bp_positive_button; - final int negativeText = enrolling ? R.string.fp_power_button_enrollment_negative_button - : R.string.fp_power_button_bp_negative_button; - - final Dialog confirmScreenOffDialog = dialogBuilder - .setTitle(title) - .setMessage(message) - .setPositiveButton(positiveText, - (dialog, which) -> { - dialog.dismiss(); - powerManager.goToSleep( - eventTime, - PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, - 0 /* flags */ - ); - }) - .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss()) - .setOnDismissListener(dismissListener) - .setCancelable(false) - .create(); - confirmScreenOffDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + final int title = + enrolling + ? R.string.fp_power_button_enrollment_title + : R.string.fp_power_button_bp_title; + final int message = + enrolling + ? R.string.fp_power_button_enrollment_message + : R.string.fp_power_button_bp_message; + final int positiveText = + enrolling + ? R.string.fp_power_button_enrollment_positive_button + : R.string.fp_power_button_bp_positive_button; + final int negativeText = + enrolling + ? R.string.fp_power_button_enrollment_negative_button + : R.string.fp_power_button_bp_negative_button; + + final Dialog confirmScreenOffDialog = + dialogBuilder + .setTitle(title) + .setMessage(message) + .setPositiveButton( + positiveText, + (dialog, which) -> { + dialog.dismiss(); + powerManager.goToSleep( + eventTime, + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, + 0 /* flags */); + }) + .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss()) + .setOnDismissListener(dismissListener) + .setCancelable(false) + .create(); + confirmScreenOffDialog + .getWindow() + .setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); confirmScreenOffDialog.show(); return confirmScreenOffDialog; } /** - * Awaits notification from PhoneWindowManager that fingerprint service is ready - * to send updates about power button fps sensor state. Then configures a - * BiometricStateListener to receive and record updates to fps state, and - * registers the BiometricStateListener in FingerprintManager. + * Awaits notification from PhoneWindowManager that fingerprint service is ready to send updates + * about power button fps sensor state. Then configures a BiometricStateListener to receive and + * record updates to fps state, and registers the BiometricStateListener in FingerprintManager. */ public void onFingerprintSensorReady() { final PackageManager pm = mContext.getPackageManager(); @@ -184,12 +247,12 @@ public class SideFpsEventHandler { if (fingerprintManager.isPowerbuttonFps()) { fingerprintManager.registerBiometricStateListener( new BiometricStateListener() { - @Nullable - private Runnable mStateRunnable = null; + @Nullable private Runnable mStateRunnable = null; @Override public void onStateChanged( @BiometricStateListener.State int newState) { + Log.d(TAG, "onStateChanged : " + newState); if (mStateRunnable != null) { mHandler.removeCallbacks(mStateRunnable); mStateRunnable = null; @@ -200,16 +263,58 @@ public class SideFpsEventHandler { // damper when moving to idle in case auth is first if (newState == STATE_IDLE) { mStateRunnable = () -> mBiometricState = newState; - mHandler.postDelayed(mStateRunnable, - DEBOUNCE_DELAY_MILLIS); + // This is also useful in the case of biometric + // prompt. + // If a user has recently succeeded/failed auth, we + // want to disable the power button for a short + // period of time (so ethey are able to view the + // prompt) + mHandler.postDelayed( + mStateRunnable, DEBOUNCE_DELAY_MILLIS); + dismissDialog("STATE_IDLE"); } else { mBiometricState = newState; } } + + @Override + public void onBiometricAction( + @BiometricStateListener.Action int action) { + Log.d(TAG, "onBiometricAction " + action); + switch (action) { + case BiometricStateListener.ACTION_SENSOR_TOUCH: + mHandler.postDelayed( + mTurnOffDialog, + mTapWaitForPowerDuration); + break; + } + } }); mSideFpsEventHandlerReady.set(true); } } }); } + + private void dismissDialog(String reason) { + Log.d(TAG, "Dismissing dialog with reason: " + reason); + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + } + + private void showDialog(long time, String reason) { + Log.d(TAG, "Showing dialog with reason: " + reason); + if (mDialog != null && mDialog.isShowing()) { + Log.d(TAG, "Ignoring show dialog"); + return; + } + mDialog = + showConfirmDialog( + mDialogSupplier.get(), + mPowerManager, + time, + mBiometricState, + mDialogDismissListener); + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index 1a49f8aa2e6a..ea1e49d8748a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -43,8 +43,10 @@ import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; @@ -128,6 +130,8 @@ public class FingerprintAuthenticationClientTest { @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; + private TestLooper mLooper = new TestLooper(); + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -233,6 +237,9 @@ public class FingerprintAuthenticationClientTest { client.start(mCallback); client.onAuthenticated(new Fingerprint("name", 2 /* enrollmentId */, SENSOR_ID), true /* authenticated */, new ArrayList<>()); + + mLooper.moveTimeForward(10); + mLooper.dispatchAll(); verify(mLuxProbe).destroy(); client.onAcquired(2, 0); @@ -309,9 +316,58 @@ public class FingerprintAuthenticationClientTest { client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */), true /* authenticated */, new ArrayList<>()); + mLooper.moveTimeForward(10); + mLooper.dispatchAll(); verify(mCancellationSignal).cancel(); } + @Test + public void fingerprintPowerIgnoresAuthInWindow() throws Exception { + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + client.onPowerPressed(); + client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + + verify(mCallback).onClientFinished(any(), eq(false)); + } + + @Test + public void fingerprintAuthIgnoredWaitingForPower() throws Exception { + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + client.onAuthenticated(new Fingerprint("friendly", 3 /* fingerId */, 4 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + client.onPowerPressed(); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + + verify(mCallback).onClientFinished(any(), eq(false)); + } + + @Test + public void fingerprintAuthSucceedsAfterPowerWindow() throws Exception { + when(mSensorProps.isAnySidefpsType()).thenReturn(true); + + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + client.onPowerPressed(); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */), + true /* authenticated */, new ArrayList<>()); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + + verify(mCallback).onClientFinished(any(), eq(true)); + } + private FingerprintAuthenticationClient createClient() throws RemoteException { return createClient(100 /* version */, true /* allowBackgroundAuthentication */); } @@ -336,7 +392,8 @@ public class FingerprintAuthenticationClientTest { 9 /* sensorId */, mBiometricLogger, mBiometricContext, true /* isStrongBiometric */, null /* taskStackListener */, mLockoutCache, - mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, mSensorProps) { + mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, mSensorProps, + new Handler(mLooper.getLooper())) { @Override protected ActivityTaskManager getActivityTaskManager() { return mActivityTaskManager; diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java index 371861f74871..7746bd6859d6 100644 --- a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java @@ -53,33 +53,30 @@ import java.util.List; /** * Unit tests for {@link SideFpsEventHandler}. - * <p/> - * Run with <code>atest SideFpsEventHandlerTest</code>. + * + * <p>Run with <code>atest SideFpsEventHandlerTest</code>. */ @SmallTest @RunWith(AndroidTestingRunner.class) public class SideFpsEventHandlerTest { - private static final List<Integer> sAllStates = List.of( - BiometricStateListener.STATE_IDLE, - BiometricStateListener.STATE_ENROLLING, - BiometricStateListener.STATE_KEYGUARD_AUTH, - BiometricStateListener.STATE_BP_AUTH, - BiometricStateListener.STATE_AUTH_OTHER); + private static final List<Integer> sAllStates = + List.of( + BiometricStateListener.STATE_IDLE, + BiometricStateListener.STATE_ENROLLING, + BiometricStateListener.STATE_KEYGUARD_AUTH, + BiometricStateListener.STATE_BP_AUTH, + BiometricStateListener.STATE_AUTH_OTHER); @Rule public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext(), null); - @Mock - private PackageManager mPackageManager; - @Mock - private FingerprintManager mFingerprintManager; - @Spy - private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext); - @Mock - private AlertDialog mAlertDialog; - @Mock - private Window mWindow; + + @Mock private PackageManager mPackageManager; + @Mock private FingerprintManager mFingerprintManager; + @Spy private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext); + @Mock private AlertDialog mAlertDialog; + @Mock private Window mWindow; private TestLooper mLooper = new TestLooper(); private SideFpsEventHandler mEventHandler; @@ -95,9 +92,12 @@ public class SideFpsEventHandlerTest { when(mDialogBuilder.create()).thenReturn(mAlertDialog); when(mAlertDialog.getWindow()).thenReturn(mWindow); - mEventHandler = new SideFpsEventHandler( - mContext, new Handler(mLooper.getLooper()), - mContext.getSystemService(PowerManager.class), () -> mDialogBuilder); + mEventHandler = + new SideFpsEventHandler( + mContext, + new Handler(mLooper.getLooper()), + mContext.getSystemService(PowerManager.class), + () -> mDialogBuilder); } @Test @@ -105,7 +105,7 @@ public class SideFpsEventHandlerTest { when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_FINGERPRINT))) .thenReturn(false); - assertThat(mEventHandler.onSinglePressDetected(60L)).isFalse(); + assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isFalse(); mLooper.dispatchAll(); verify(mAlertDialog, never()).show(); @@ -117,7 +117,7 @@ public class SideFpsEventHandlerTest { for (int state : sAllStates) { setBiometricState(state); - assertThat(mEventHandler.onSinglePressDetected(200L)).isFalse(); + assertThat(mEventHandler.shouldConsumeSinglePress(200L)).isFalse(); mLooper.dispatchAll(); verify(mAlertDialog, never()).show(); @@ -130,7 +130,7 @@ public class SideFpsEventHandlerTest { for (int state : sAllStates) { setBiometricState(state); - assertThat(mEventHandler.onSinglePressDetected(400L)).isFalse(); + assertThat(mEventHandler.shouldConsumeSinglePress(400L)).isFalse(); mLooper.dispatchAll(); verify(mAlertDialog, never()).show(); @@ -139,13 +139,13 @@ public class SideFpsEventHandlerTest { @Test public void ignoresWhenIdleOrUnknown() throws Exception { - setupWithSensor(true /* hasSfps */, true /* initialized */); + setupWithSensor(true /* hasSidefps */, true /* initialized */); setBiometricState(BiometricStateListener.STATE_IDLE); - assertThat(mEventHandler.onSinglePressDetected(80000L)).isFalse(); + assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isFalse(); setBiometricState(BiometricStateListener.STATE_AUTH_OTHER); - assertThat(mEventHandler.onSinglePressDetected(90000L)).isFalse(); + assertThat(mEventHandler.shouldConsumeSinglePress(90000L)).isFalse(); mLooper.dispatchAll(); verify(mAlertDialog, never()).show(); @@ -156,7 +156,7 @@ public class SideFpsEventHandlerTest { setupWithSensor(true /* hasSfps */, true /* initialized */); setBiometricState(BiometricStateListener.STATE_KEYGUARD_AUTH); - assertThat(mEventHandler.onSinglePressDetected(80000L)).isFalse(); + assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isFalse(); mLooper.dispatchAll(); verify(mAlertDialog, never()).show(); @@ -164,13 +164,13 @@ public class SideFpsEventHandlerTest { @Test public void promptsWhenBPisActive() throws Exception { - setupWithSensor(true /* hasSfps */, true /* initialized */); + setupWithSensor(true /* hasSideFps */, true /* initialized */); setBiometricState(BiometricStateListener.STATE_BP_AUTH); - assertThat(mEventHandler.onSinglePressDetected(80000L)).isTrue(); + assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue(); mLooper.dispatchAll(); - verify(mAlertDialog).show(); + verify(mAlertDialog, never()).show(); } @Test @@ -178,7 +178,57 @@ public class SideFpsEventHandlerTest { setupWithSensor(true /* hasSfps */, true /* initialized */); setBiometricState(BiometricStateListener.STATE_ENROLLING); - assertThat(mEventHandler.onSinglePressDetected(80000L)).isTrue(); + assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue(); + + mLooper.dispatchAll(); + verify(mAlertDialog).show(); + verify(mAlertDialog, never()).dismiss(); + } + + @Test + public void dismissesDialogOnTouchWhenEnrolling() throws Exception { + setupWithSensor(true /* hasSfps */, true /* initialized */); + + setBiometricState(BiometricStateListener.STATE_ENROLLING); + when(mAlertDialog.isShowing()).thenReturn(true); + assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue(); + + mLooper.dispatchAll(); + verify(mAlertDialog).show(); + + mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH); + mLooper.moveTimeForward(10000); + mLooper.dispatchAll(); + + verify(mAlertDialog).dismiss(); + } + + @Test + public void dismissesDialogFailsWhenPowerPressedAndDialogShowing() throws Exception { + setupWithSensor(true /* hasSfps */, true /* initialized */); + + setBiometricState(BiometricStateListener.STATE_ENROLLING); + when(mAlertDialog.isShowing()).thenReturn(true); + assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue(); + + mLooper.dispatchAll(); + verify(mAlertDialog).show(); + + mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH); + assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue(); + + mLooper.dispatchAll(); + verify(mAlertDialog, never()).dismiss(); + } + + @Test + public void showDialogAfterTap() throws Exception { + setupWithSensor(true /* hasSfps */, true /* initialized */); + + setBiometricState(BiometricStateListener.STATE_ENROLLING); + when(mAlertDialog.isShowing()).thenReturn(true); + mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH); + assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue(); mLooper.dispatchAll(); verify(mAlertDialog).show(); @@ -201,11 +251,13 @@ public class SideFpsEventHandlerTest { ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(fpCallbackCaptor.capture()); if (initialized) { - fpCallbackCaptor.getValue().onAllAuthenticatorsRegistered( - List.of(mock(FingerprintSensorPropertiesInternal.class))); + fpCallbackCaptor + .getValue() + .onAllAuthenticatorsRegistered( + List.of(mock(FingerprintSensorPropertiesInternal.class))); if (hasSfps) { - ArgumentCaptor<BiometricStateListener> captor = ArgumentCaptor.forClass( - BiometricStateListener.class); + ArgumentCaptor<BiometricStateListener> captor = + ArgumentCaptor.forClass(BiometricStateListener.class); verify(mFingerprintManager).registerBiometricStateListener(captor.capture()); mBiometricStateListener = captor.getValue(); } |