diff options
5 files changed, 102 insertions, 21 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index b4c6321fe191..7adc147784d7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1359,7 +1359,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:AOD_INTERRUPT_END"); } - mAuthController.onCancelAodInterrupt(); + mAuthController.onCancelUdfps(); mIsUdfpsRunningWhileDozing = false; } }; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index ed8f32f31035..0c7b55d6ecbb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -309,18 +309,14 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } /** - * Cancel a fingerprint scan. - * - * The sensor that triggers an AOD interrupt for fingerprint doesn't give - * ACTION_UP/ACTION_CANCEL events, so the scan needs to be cancelled manually. This should be - * called when authentication either succeeds or fails. Failing to cancel the scan will leave - * the screen in high brightness mode. + * Cancel a fingerprint scan manually. This will get rid of the white circle on the udfps + * sensor area even if the user hasn't explicitly lifted their finger yet. */ - public void onCancelAodInterrupt() { + public void onCancelUdfps() { if (mUdfpsController == null) { return; } - mUdfpsController.onCancelAodInterrupt(); + mUdfpsController.onCancelUdfps(); } private void sendResultAndCleanUp(@DismissedReason int reason, @@ -499,6 +495,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage); mCurrentDialog.onError(errorMessage); } + + onCancelUdfps(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index aa818bfdadeb..9239a8ade615 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -35,15 +36,23 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; +import android.media.AudioAttributes; +import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.VelocityTracker; +import android.view.View; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; @@ -94,7 +103,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @NonNull private final DumpManager mDumpManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final KeyguardViewMediator mKeyguardViewMediator; - @NonNull private FalsingManager mFalsingManager; + @NonNull private final Vibrator mVibrator; + @NonNull private final Handler mMainHandler; + @NonNull private final FalsingManager mFalsingManager; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -118,6 +129,27 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private boolean mIsAodInterruptActive; @Nullable private Runnable mCancelAodTimeoutAction; + private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = + new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .build(); + + private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK); + private final VibrationEffect mEffectTextureTick = + VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); + private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + private final VibrationEffect mEffectHeavy = + VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + private final Runnable mAcquiredVibration = new Runnable() { + @Override + public void run() { + String effect = Settings.Global.getString(mContext.getContentResolver(), + "udfps_acquired_type"); + mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES); + } + }; + /** * Keeps track of state within a single FingerprintService request. Note that this state * persists across configuration changes, etc, since it is considered a single request. @@ -227,7 +259,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { }; @SuppressLint("ClickableViewAccessibility") - private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) -> { + private final UdfpsView.OnTouchListener mOnTouchListener = this::onTouch; + + private boolean onTouch(View view, MotionEvent event) { UdfpsView udfpsView = (UdfpsView) view; final boolean isFingerDown = udfpsView.isIlluminationRequested(); boolean handled = false; @@ -251,6 +285,27 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // data for many other pointers because of multi-touch support. mActivePointerId = event.getPointerId(0); mVelocityTracker.addMovement(event); + + // TODO: (b/185124905) these settings are for ux testing purposes and should + // be removed (or cached) before going into production + final ContentResolver contentResolver = mContext.getContentResolver(); + int startEnabled = Settings.Global.getInt(contentResolver, + "udfps_start", 0); + if (startEnabled > 0) { + String startEffectSetting = Settings.Global.getString(contentResolver, + "udfps_start_type"); + mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick), + VIBRATION_SONIFICATION_ATTRIBUTES); + } + + int acquiredEnabled = Settings.Global.getInt(contentResolver, + "udfps_acquired", 0); + if (acquiredEnabled > 0) { + int delay = Settings.Global.getInt(contentResolver, + "udfps_acquired_delay", 500); + mMainHandler.removeCallbacks(mAcquiredVibration); + mMainHandler.postDelayed(mAcquiredVibration, delay); + } handled = true; } break; @@ -307,7 +362,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Do nothing. } return handled; - }; + } @Inject public UdfpsController(@NonNull Context context, @@ -324,6 +379,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @NonNull KeyguardViewMediator keyguardViewMediator, @NonNull FalsingManager falsingManager) { mContext = context; + // TODO (b/185124905): inject main handler and vibrator once done prototyping + mMainHandler = new Handler(Looper.getMainLooper()); + mVibrator = context.getSystemService(Vibrator.class); mInflater = inflater; // The fingerprint manager is queried for UDFPS before this class is constructed, so the // fingerprint manager should never be null. @@ -559,19 +617,25 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL, // we need to be careful about not letting the screen accidentally remain in high brightness // mode. As a mitigation, queue a call to cancel the fingerprint scan. - mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelAodInterrupt, + mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps, AOD_INTERRUPT_TIMEOUT_MILLIS); // using a hard-coded value for major and minor until it is available from the sensor onFingerDown(screenX, screenY, minor, major); } /** - * Cancel fingerprint scan. + * Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before + * user explicitly lifts their finger. Generally, this should be called whenever udfps fails + * or errors. * - * This is intended to be called after the fingerprint scan triggered by the AOD interrupt - * either succeeds or fails. + * The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give + * ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually. + * This should be called when authentication either succeeds or fails. Failing to cancel the + * scan will leave the screen in high brightness mode and will show the HbmSurfaceView until + * the user lifts their finger. */ - void onCancelAodInterrupt() { + void onCancelUdfps() { + onFingerUp(); if (!mIsAodInterruptActive) { return; } @@ -580,7 +644,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mCancelAodTimeoutAction = null; } mIsAodInterruptActive = false; - onFingerUp(); } // This method can be called from the UI thread. @@ -598,6 +661,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // This method can be called from the UI thread. private void onFingerUp() { + mMainHandler.removeCallbacks(mAcquiredVibration); if (mView == null) { Log.w(TAG, "Null view in onFingerUp"); return; @@ -617,4 +681,23 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } + + private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { + if (TextUtils.isEmpty(effect)) { + return defaultEffect; + } + + switch (effect.toLowerCase()) { + case "click": + return mEffectClick; + case "heavy": + return mEffectHeavy; + case "texture_tick": + return mEffectTextureTick; + case "tick": + return mEffectTick; + default: + return defaultEffect; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index d544f7378f8a..42af366ad8fa 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -537,7 +537,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { authCallback.onAuthenticationFailed(); // THEN aod interrupt is cancelled - verify(mAuthController).onCancelAodInterrupt(); + verify(mAuthController).onCancelUdfps(); } @Test @@ -557,7 +557,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { authCallback.onAuthenticationError(0, ""); // THEN aod interrupt is cancelled - verify(mAuthController).onCancelAodInterrupt(); + verify(mAuthController).onCancelUdfps(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index bbd3ce89b997..0aa182fb1e81 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -259,7 +259,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it is cancelled - mUdfpsController.onCancelAodInterrupt(); + mUdfpsController.onCancelUdfps(); // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } |