diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 47c4a63b4a8c1..7c779dbdd5339 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -143,6 +143,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private final VibrationEffect mEffectHeavy = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + private final VibrationEffect mDoubleClick = + VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); private final Runnable mAcquiredVibration = new Runnable() { @Override public void run() { @@ -714,6 +716,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return mEffectTextureTick; case "tick": return mEffectTick; + case "double_tap": + return mDoubleClick; default: return defaultEffect; } 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 f7443740943a3..fbf249237415e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricConstants; import android.media.AudioAttributes; @@ -26,6 +27,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.VibrationEffect; import android.os.Vibrator; +import android.text.TextUtils; import android.util.Slog; /** @@ -43,6 +45,15 @@ public abstract class AcquisitionClient extends HalClientMonitor implement .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 VibrationEffect mDoubleClick = + VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + private final PowerManager mPowerManager; private final VibrationEffect mSuccessVibrationEffect; private final VibrationEffect mErrorVibrationEffect; @@ -61,8 +72,8 @@ public abstract class AcquisitionClient extends HalClientMonitor implement super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality, statsAction, statsClient); mPowerManager = context.getSystemService(PowerManager.class); - mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + mSuccessVibrationEffect = mEffectClick; + mErrorVibrationEffect = mDoubleClick; } @Override @@ -181,18 +192,47 @@ public abstract class AcquisitionClient extends HalClientMonitor implement mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); } + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + return mSuccessVibrationEffect; + } + + protected @NonNull VibrationEffect getErrorVibrationEffect() { + return mErrorVibrationEffect; + } protected final void vibrateSuccess() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { - vibrator.vibrate(mSuccessVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES); + vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); } } protected final void vibrateError() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { - vibrator.vibrate(mErrorVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES); + vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); + } + } + + protected final @NonNull VibrationEffect getVibration(@Nullable String effect, + @NonNull 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; + case "double_click": + return mDoubleClick; + default: + return defaultEffect; } } } 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 cf545f37bd431..86688287cc9d0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -22,6 +22,7 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.hardware.biometrics.BiometricAuthenticator; @@ -30,6 +31,8 @@ import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; +import android.os.VibrationEffect; +import android.provider.Settings; import android.security.KeyStore; import android.util.EventLog; import android.util.Slog; @@ -56,12 +59,14 @@ public abstract class AuthenticationClient extends AcquisitionClient private final LockoutTracker mLockoutTracker; private final boolean mIsRestricted; private final boolean mAllowBackgroundAuthentication; + @NonNull private final ContentResolver mContentResolver; protected final long mOperationId; private long mStartTimeMs; protected boolean mAuthAttempted; + private final boolean mCustomHaptics; public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @@ -80,6 +85,10 @@ public abstract class AuthenticationClient extends AcquisitionClient mLockoutTracker = lockoutTracker; mIsRestricted = restricted; mAllowBackgroundAuthentication = allowBackgroundAuthentication; + + mContentResolver = context.getContentResolver(); + mCustomHaptics = Settings.Global.getInt(mContentResolver, + "fp_custom_success_error", 0) == 1; } public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) { @@ -333,4 +342,25 @@ public abstract class AuthenticationClient extends AcquisitionClient public boolean interruptsPrecedingClients() { return true; } + + @Override + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + if (!mCustomHaptics) { + return super.getSuccessVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "fp_success_type"), super.getSuccessVibrationEffect()); + } + + @Override + protected @NonNull VibrationEffect getErrorVibrationEffect() { + if (!mCustomHaptics) { + return super.getErrorVibrationEffect(); + } + + 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 0c883b047fe40..d04c17cdb6d3d 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 @@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.NotificationManager; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricAuthenticator; @@ -32,6 +33,8 @@ import android.hardware.face.FaceAuthenticationFrame; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; +import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Slog; import com.android.internal.R; @@ -57,6 +60,9 @@ class FaceAuthenticationClient extends AuthenticationClient implements @Nullable private final NotificationManager mNotificationManager; @Nullable private ICancellationSignal mCancellationSignal; + @NonNull private final ContentResolver mContentResolver; + private final boolean mCustomHaptics; + private final int[] mBiometricPromptIgnoreList; private final int[] mBiometricPromptIgnoreListVendor; private final int[] mKeyguardIgnoreList; @@ -87,6 +93,10 @@ class FaceAuthenticationClient extends AuthenticationClient implements R.array.config_face_acquire_keyguard_ignorelist); mKeyguardIgnoreListVendor = resources.getIntArray( R.array.config_face_acquire_vendor_keyguard_ignorelist); + + mContentResolver = context.getContentResolver(); + mCustomHaptics = Settings.Global.getInt(mContentResolver, + "face_custom_success_error", 0) == 1; } @Override @@ -243,4 +253,24 @@ class FaceAuthenticationClient extends AuthenticationClient implements Slog.e(TAG, "Remote exception", e); } } + + @Override + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + if (!mCustomHaptics) { + return super.getSuccessVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_success_type"), super.getSuccessVibrationEffect()); + } + + @Override + protected @NonNull VibrationEffect getErrorVibrationEffect() { + if (!mCustomHaptics) { + return super.getErrorVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_error_type"), super.getErrorVibrationEffect()); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index ff06a4a30a800..c4bdb320ddef6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricAuthenticator; @@ -27,6 +28,8 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; +import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Slog; import com.android.internal.R; @@ -47,7 +50,8 @@ class FaceAuthenticationClient extends AuthenticationClient { private static final String TAG = "FaceAuthenticationClient"; - + @NonNull private final ContentResolver mContentResolver; + private final boolean mCustomHaptics; private final UsageStats mUsageStats; private final int[] mBiometricPromptIgnoreList; @@ -78,6 +82,10 @@ class FaceAuthenticationClient extends AuthenticationClient { R.array.config_face_acquire_keyguard_ignorelist); mKeyguardIgnoreListVendor = resources.getIntArray( R.array.config_face_acquire_vendor_keyguard_ignorelist); + + mContentResolver = context.getContentResolver(); + mCustomHaptics = Settings.Global.getInt(mContentResolver, + "face_custom_success_error", 0) == 1; } @Override @@ -188,4 +196,24 @@ class FaceAuthenticationClient extends AuthenticationClient { final boolean shouldSend = shouldSend(acquireInfo, vendorCode); onAcquiredInternal(acquireInfo, vendorCode, shouldSend); } + + @Override + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + if (!mCustomHaptics) { + return super.getSuccessVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_success_type"), super.getSuccessVibrationEffect()); + } + + @Override + protected @NonNull VibrationEffect getErrorVibrationEffect() { + if (!mCustomHaptics) { + return super.getErrorVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_error_type"), super.getErrorVibrationEffect()); + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index c5ed20afacecc..4d1f241787a75 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -37,6 +37,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; @@ -47,6 +48,7 @@ import com.android.server.biometrics.nano.BiometricsProto; import com.android.server.biometrics.sensors.BiometricScheduler.Operation; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -62,11 +64,13 @@ public class BiometricSchedulerTest { private BiometricScheduler mScheduler; private IBinder mToken; - @Mock - private Context mContext; @Mock private IBiometricService mBiometricService; + @Rule + public final TestableContext mContext = + new TestableContext(InstrumentationRegistry.getContext(), null); + @Before public void setUp() { MockitoAnnotations.initMocks(this);