From 6b0285cc7cc3417217d7e11784a6ebb1c4d913d3 Mon Sep 17 00:00:00 2001 From: Beverly Date: Thu, 24 Jun 2021 17:57:18 -0400 Subject: [PATCH] Update configurable udfps haptics * Hooks up onAcquired haptic to the actual onAcquired callback (instead of hardcoded delay) * Adds the ability to change the haptic type to a PRIMITIVE type * Adds the ability to disable fp success/error haptics separately. Test: manual, atest SystemUITest Bug: 185124905 Change-Id: I2d174b4f7c466fb93109c9aabbb327f94032fab1 --- .../keyguard/KeyguardUpdateMonitor.java | 96 ++++++++++++------- .../systemui/biometrics/UdfpsController.java | 61 ++++++------ .../keyguard/KeyguardUpdateMonitorTest.java | 7 +- .../biometrics/UdfpsControllerTest.java | 2 +- .../biometrics/sensors/AcquisitionClient.java | 14 +-- .../sensors/AuthenticationClient.java | 12 ++- .../face/aidl/FaceAuthenticationClient.java | 4 +- 7 files changed, 118 insertions(+), 78 deletions(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 38f8f7ac321f7..b3f6b7f4247e9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -71,6 +71,7 @@ import android.os.ServiceManager; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.os.Vibrator; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; @@ -85,6 +86,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; +import androidx.annotation.Nullable; import androidx.lifecycle.Observer; import com.android.internal.annotations.VisibleForTesting; @@ -95,6 +97,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -282,6 +285,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected boolean mTelephonyCapable; + private final boolean mAcquiredHapticEnabled; + @Nullable private final Vibrator mVibrator; + // Device provisioning state private boolean mDeviceProvisioned; @@ -1334,44 +1340,66 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback = new AuthenticationCallback() { + private boolean mPlayedAcquiredHaptic; - @Override - public void onAuthenticationFailed() { - handleFingerprintAuthFailed(); - } + @Override + public void onAuthenticationFailed() { + handleFingerprintAuthFailed(); + } - @Override - public void onAuthenticationSucceeded(AuthenticationResult result) { - Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); - handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric()); - Trace.endSection(); - } + @Override + public void onAuthenticationSucceeded(AuthenticationResult result) { + Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); + handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric()); + Trace.endSection(); - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - handleFingerprintHelp(helpMsgId, helpString.toString()); - } + // on auth success, we sometimes never received an acquired haptic + if (!mPlayedAcquiredHaptic) { + playAcquiredHaptic(); + } + } - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) { - handleFingerprintError(errMsgId, errString.toString()); - } + @Override + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + handleFingerprintHelp(helpMsgId, helpString.toString()); + } - @Override - public void onAuthenticationAcquired(int acquireInfo) { - handleFingerprintAcquired(acquireInfo); - } + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + handleFingerprintError(errMsgId, errString.toString()); + } - @Override - public void onUdfpsPointerDown(int sensorId) { - Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId); - } + @Override + public void onAuthenticationAcquired(int acquireInfo) { + handleFingerprintAcquired(acquireInfo); + if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) { + playAcquiredHaptic(); + } + } - @Override - public void onUdfpsPointerUp(int sensorId) { - Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId); - } - }; + @Override + public void onUdfpsPointerDown(int sensorId) { + Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId); + mPlayedAcquiredHaptic = false; + } + + @Override + public void onUdfpsPointerUp(int sensorId) { + Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId); + } + + private void playAcquiredHaptic() { + if (mAcquiredHapticEnabled && mVibrator != null && isUdfpsEnrolled()) { + mPlayedAcquiredHaptic = true; + String effect = Settings.Global.getString( + mContext.getContentResolver(), + "udfps_acquired_type"); + mVibrator.vibrate(UdfpsController.getVibration(effect, + UdfpsController.EFFECT_TICK), + UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); + } + } + }; private final FaceManager.FaceDetectionCallback mFaceDetectionCallback = (sensorId, userId, isStrongBiometric) -> { @@ -1663,7 +1691,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab LockPatternUtils lockPatternUtils, AuthController authController, TelephonyListenerManager telephonyListenerManager, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + @Nullable Vibrator vibrator) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mTelephonyListenerManager = telephonyListenerManager; @@ -1678,6 +1707,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockPatternUtils = lockPatternUtils; mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); + mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(), + "udfps_acquired", 0) == 1; + mVibrator = vibrator; mHandler = new Handler(mainLooper) { @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 11412f41f578c..aec5a579c9f3e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; + import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; @@ -145,32 +147,22 @@ public class UdfpsController implements DozeReceiver { private Runnable mAodInterruptRunnable; @VisibleForTesting - static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = + public 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 = + public static final VibrationEffect EFFECT_TICK = + VibrationEffect.get(VibrationEffect.EFFECT_TICK); + private static final VibrationEffect EFFECT_TEXTURE_TICK = VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); @VisibleForTesting - final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - private final VibrationEffect mEffectHeavy = + static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + private static final VibrationEffect EFFECT_HEAVY = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); - private final VibrationEffect mDoubleClick = + private static final VibrationEffect EFFECT_DOUBLE_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); - private final Runnable mAcquiredVibration = new Runnable() { - @Override - public void run() { - if (mVibrator == null) { - return; - } - String effect = Settings.Global.getString(mContext.getContentResolver(), - "udfps_acquired_type"); - mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES); - } - }; private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override @@ -447,16 +439,7 @@ public class UdfpsController implements DozeReceiver { 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); + EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES); } } @@ -839,7 +822,6 @@ public class UdfpsController implements DozeReceiver { private void onFingerUp() { mActivePointerId = -1; mGoodCaptureReceived = false; - mMainHandler.removeCallbacks(mAcquiredVibration); if (mView == null) { Log.w(TAG, "Null view in onFingerUp"); return; @@ -851,23 +833,34 @@ public class UdfpsController implements DozeReceiver { } - private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { + /** + * get vibration to play given string + * used for testing purposes (b/185124905) + */ + public static VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { if (TextUtils.isEmpty(effect)) { return defaultEffect; } switch (effect.toLowerCase()) { case "click": - return mEffectClick; + return EFFECT_CLICK; case "heavy": - return mEffectHeavy; + return EFFECT_HEAVY; case "texture_tick": - return mEffectTextureTick; + return EFFECT_TEXTURE_TICK; case "tick": - return mEffectTick; + return EFFECT_TICK; case "double_tap": - return mDoubleClick; + return EFFECT_DOUBLE_CLICK; default: + try { + int primitive = Integer.parseInt(effect); + if (primitive <= PRIMITIVE_LOW_TICK && primitive > -1) { + return VibrationEffect.startComposition().addPrimitive(primitive).compose(); + } + } catch (NumberFormatException e) { + } 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 3d4da270dd448..db9b9437b87b0 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.UserHandle; import android.os.UserManager; +import android.os.Vibrator; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -108,6 +109,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -168,6 +170,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private TelephonyListenerManager mTelephonyListenerManager; @Mock private FeatureFlags mFeatureFlags; + @Mock + private Vibrator mVibrator; @Captor private ArgumentCaptor mStatusBarStateListenerCaptor; // Direct executor @@ -979,7 +983,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mBroadcastDispatcher, mDumpManager, mRingerModeTracker, mBackgroundExecutor, mStatusBarStateController, mLockPatternUtils, - mAuthController, mTelephonyListenerManager, mFeatureFlags); + mAuthController, mTelephonyListenerManager, mFeatureFlags, + mVibrator); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } 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 0c750a179358d..9873b2016fb6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -338,7 +338,7 @@ public class UdfpsControllerTest extends SysuiTestCase { moveEvent.recycle(); // THEN click haptic is played - verify(mVibrator).vibrate(mUdfpsController.mEffectClick, + verify(mVibrator).vibrate(mUdfpsController.EFFECT_CLICK, UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index fbf249237415e..28e23e32c8db2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -192,25 +192,27 @@ public abstract class AcquisitionClient extends HalClientMonitor implement mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); } - protected @NonNull VibrationEffect getSuccessVibrationEffect() { + protected @Nullable VibrationEffect getSuccessVibrationEffect() { return mSuccessVibrationEffect; } - protected @NonNull VibrationEffect getErrorVibrationEffect() { + protected @Nullable VibrationEffect getErrorVibrationEffect() { return mErrorVibrationEffect; } protected final void vibrateSuccess() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); - if (vibrator != null) { - vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); + VibrationEffect effect = getSuccessVibrationEffect(); + if (vibrator != null && effect != null) { + vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES); } } protected final void vibrateError() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); - if (vibrator != null) { - vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); + VibrationEffect effect = getErrorVibrationEffect(); + if (vibrator != null && effect != null) { + vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 86688287cc9d0..6b9ff6f35128a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -344,21 +344,29 @@ public abstract class AuthenticationClient extends AcquisitionClient } @Override - protected @NonNull VibrationEffect getSuccessVibrationEffect() { + protected @Nullable VibrationEffect getSuccessVibrationEffect() { if (!mCustomHaptics) { return super.getSuccessVibrationEffect(); } + if (Settings.Global.getInt(mContentResolver, "fp_success_enabled", 1) == 0) { + return null; + } + return getVibration(Settings.Global.getString(mContentResolver, "fp_success_type"), super.getSuccessVibrationEffect()); } @Override - protected @NonNull VibrationEffect getErrorVibrationEffect() { + protected @Nullable VibrationEffect getErrorVibrationEffect() { if (!mCustomHaptics) { return super.getErrorVibrationEffect(); } + if (Settings.Global.getInt(mContentResolver, "fp_error_enabled", 1) == 0) { + return null; + } + return getVibration(Settings.Global.getString(mContentResolver, "fp_error_type"), super.getErrorVibrationEffect()); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 2adf5f98cee5b..2156f6462bf81 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -264,7 +264,7 @@ class FaceAuthenticationClient extends AuthenticationClient implements } @Override - protected @NonNull VibrationEffect getSuccessVibrationEffect() { + protected @Nullable VibrationEffect getSuccessVibrationEffect() { if (!mCustomHaptics) { return super.getSuccessVibrationEffect(); } @@ -274,7 +274,7 @@ class FaceAuthenticationClient extends AuthenticationClient implements } @Override - protected @NonNull VibrationEffect getErrorVibrationEffect() { + protected @Nullable VibrationEffect getErrorVibrationEffect() { if (!mCustomHaptics) { return super.getErrorVibrationEffect(); }