Merge "Update configurable udfps haptics" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-06-25 19:12:17 +00:00
committed by Android (Google) Code Review
7 changed files with 118 additions and 78 deletions

View File

@@ -71,6 +71,7 @@ import android.os.ServiceManager;
import android.os.Trace; import android.os.Trace;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings; import android.provider.Settings;
import android.service.dreams.DreamService; import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager; import android.service.dreams.IDreamManager;
@@ -85,6 +86,7 @@ import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -95,6 +97,7 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable; import com.android.systemui.Dumpable;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Background;
@@ -282,6 +285,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@VisibleForTesting @VisibleForTesting
protected boolean mTelephonyCapable; protected boolean mTelephonyCapable;
private final boolean mAcquiredHapticEnabled;
@Nullable private final Vibrator mVibrator;
// Device provisioning state // Device provisioning state
private boolean mDeviceProvisioned; private boolean mDeviceProvisioned;
@@ -1334,6 +1340,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() { = new AuthenticationCallback() {
private boolean mPlayedAcquiredHaptic;
@Override @Override
public void onAuthenticationFailed() { public void onAuthenticationFailed() {
@@ -1345,6 +1352,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric()); handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric());
Trace.endSection(); Trace.endSection();
// on auth success, we sometimes never received an acquired haptic
if (!mPlayedAcquiredHaptic) {
playAcquiredHaptic();
}
} }
@Override @Override
@@ -1360,17 +1372,33 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override @Override
public void onAuthenticationAcquired(int acquireInfo) { public void onAuthenticationAcquired(int acquireInfo) {
handleFingerprintAcquired(acquireInfo); handleFingerprintAcquired(acquireInfo);
if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
playAcquiredHaptic();
}
} }
@Override @Override
public void onUdfpsPointerDown(int sensorId) { public void onUdfpsPointerDown(int sensorId) {
Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId); Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId);
mPlayedAcquiredHaptic = false;
} }
@Override @Override
public void onUdfpsPointerUp(int sensorId) { public void onUdfpsPointerUp(int sensorId) {
Log.d(TAG, "onUdfpsPointerUp, sensorId: " + 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 private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
@@ -1663,7 +1691,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
LockPatternUtils lockPatternUtils, LockPatternUtils lockPatternUtils,
AuthController authController, AuthController authController,
TelephonyListenerManager telephonyListenerManager, TelephonyListenerManager telephonyListenerManager,
FeatureFlags featureFlags) { FeatureFlags featureFlags,
@Nullable Vibrator vibrator) {
mContext = context; mContext = context;
mSubscriptionManager = SubscriptionManager.from(context); mSubscriptionManager = SubscriptionManager.from(context);
mTelephonyListenerManager = telephonyListenerManager; mTelephonyListenerManager = telephonyListenerManager;
@@ -1678,6 +1707,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLockPatternUtils = lockPatternUtils; mLockPatternUtils = lockPatternUtils;
mAuthController = authController; mAuthController = authController;
dumpManager.registerDumpable(getClass().getName(), this); dumpManager.registerDumpable(getClass().getName(), this);
mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(),
"udfps_acquired", 0) == 1;
mVibrator = vibrator;
mHandler = new Handler(mainLooper) { mHandler = new Handler(mainLooper) {
@Override @Override

View File

@@ -16,6 +16,8 @@
package com.android.systemui.biometrics; 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.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
@@ -147,32 +149,22 @@ public class UdfpsController implements DozeReceiver {
private Runnable mAodInterruptRunnable; private Runnable mAodInterruptRunnable;
@VisibleForTesting @VisibleForTesting
static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder() new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build(); .build();
private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK); public static final VibrationEffect EFFECT_TICK =
private final VibrationEffect mEffectTextureTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
private static final VibrationEffect EFFECT_TEXTURE_TICK =
VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
@VisibleForTesting @VisibleForTesting
final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
private final VibrationEffect mEffectHeavy = private static final VibrationEffect EFFECT_HEAVY =
VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
private final VibrationEffect mDoubleClick = private static final VibrationEffect EFFECT_DOUBLE_CLICK =
VibrationEffect.get(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() { private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override @Override
@@ -449,16 +441,7 @@ public class UdfpsController implements DozeReceiver {
String startEffectSetting = Settings.Global.getString( String startEffectSetting = Settings.Global.getString(
contentResolver, "udfps_start_type"); contentResolver, "udfps_start_type");
mVibrator.vibrate(getVibration(startEffectSetting, mVibrator.vibrate(getVibration(startEffectSetting,
mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES); EFFECT_CLICK), 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);
} }
} }
@@ -843,7 +826,6 @@ public class UdfpsController implements DozeReceiver {
mExecution.assertIsMainThread(); mExecution.assertIsMainThread();
mActivePointerId = -1; mActivePointerId = -1;
mGoodCaptureReceived = false; mGoodCaptureReceived = false;
mMainHandler.removeCallbacks(mAcquiredVibration);
if (mView == null) { if (mView == null) {
Log.w(TAG, "Null view in onFingerUp"); Log.w(TAG, "Null view in onFingerUp");
return; return;
@@ -855,23 +837,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)) { if (TextUtils.isEmpty(effect)) {
return defaultEffect; return defaultEffect;
} }
switch (effect.toLowerCase()) { switch (effect.toLowerCase()) {
case "click": case "click":
return mEffectClick; return EFFECT_CLICK;
case "heavy": case "heavy":
return mEffectHeavy; return EFFECT_HEAVY;
case "texture_tick": case "texture_tick":
return mEffectTextureTick; return EFFECT_TEXTURE_TICK;
case "tick": case "tick":
return mEffectTick; return EFFECT_TICK;
case "double_tap": case "double_tap":
return mDoubleClick; return EFFECT_DOUBLE_CLICK;
default: 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; return defaultEffect;
} }
} }

View File

@@ -64,6 +64,7 @@ import android.os.Handler;
import android.os.IRemoteCallback; import android.os.IRemoteCallback;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.os.Vibrator;
import android.telephony.ServiceState; import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
@@ -108,6 +109,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper @TestableLooper.RunWithLooper
@@ -168,6 +170,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private TelephonyListenerManager mTelephonyListenerManager; private TelephonyListenerManager mTelephonyListenerManager;
@Mock @Mock
private FeatureFlags mFeatureFlags; private FeatureFlags mFeatureFlags;
@Mock
private Vibrator mVibrator;
@Captor @Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
// Direct executor // Direct executor
@@ -979,7 +983,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mBroadcastDispatcher, mDumpManager, mBroadcastDispatcher, mDumpManager,
mRingerModeTracker, mBackgroundExecutor, mRingerModeTracker, mBackgroundExecutor,
mStatusBarStateController, mLockPatternUtils, mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager, mFeatureFlags); mAuthController, mTelephonyListenerManager, mFeatureFlags,
mVibrator);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
} }

View File

@@ -344,7 +344,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
moveEvent.recycle(); moveEvent.recycle();
// THEN click haptic is played // THEN click haptic is played
verify(mVibrator).vibrate(mUdfpsController.mEffectClick, verify(mVibrator).vibrate(mUdfpsController.EFFECT_CLICK,
UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
} }
} }

View File

@@ -192,25 +192,27 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
} }
protected @NonNull VibrationEffect getSuccessVibrationEffect() { protected @Nullable VibrationEffect getSuccessVibrationEffect() {
return mSuccessVibrationEffect; return mSuccessVibrationEffect;
} }
protected @NonNull VibrationEffect getErrorVibrationEffect() { protected @Nullable VibrationEffect getErrorVibrationEffect() {
return mErrorVibrationEffect; return mErrorVibrationEffect;
} }
protected final void vibrateSuccess() { protected final void vibrateSuccess() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class); Vibrator vibrator = getContext().getSystemService(Vibrator.class);
if (vibrator != null) { VibrationEffect effect = getSuccessVibrationEffect();
vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); if (vibrator != null && effect != null) {
vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES);
} }
} }
protected final void vibrateError() { protected final void vibrateError() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class); Vibrator vibrator = getContext().getSystemService(Vibrator.class);
if (vibrator != null) { VibrationEffect effect = getErrorVibrationEffect();
vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); if (vibrator != null && effect != null) {
vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES);
} }
} }

View File

@@ -344,21 +344,29 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
} }
@Override @Override
protected @NonNull VibrationEffect getSuccessVibrationEffect() { protected @Nullable VibrationEffect getSuccessVibrationEffect() {
if (!mCustomHaptics) { if (!mCustomHaptics) {
return super.getSuccessVibrationEffect(); return super.getSuccessVibrationEffect();
} }
if (Settings.Global.getInt(mContentResolver, "fp_success_enabled", 1) == 0) {
return null;
}
return getVibration(Settings.Global.getString(mContentResolver, return getVibration(Settings.Global.getString(mContentResolver,
"fp_success_type"), super.getSuccessVibrationEffect()); "fp_success_type"), super.getSuccessVibrationEffect());
} }
@Override @Override
protected @NonNull VibrationEffect getErrorVibrationEffect() { protected @Nullable VibrationEffect getErrorVibrationEffect() {
if (!mCustomHaptics) { if (!mCustomHaptics) {
return super.getErrorVibrationEffect(); return super.getErrorVibrationEffect();
} }
if (Settings.Global.getInt(mContentResolver, "fp_error_enabled", 1) == 0) {
return null;
}
return getVibration(Settings.Global.getString(mContentResolver, return getVibration(Settings.Global.getString(mContentResolver,
"fp_error_type"), super.getErrorVibrationEffect()); "fp_error_type"), super.getErrorVibrationEffect());

View File

@@ -264,7 +264,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
} }
@Override @Override
protected @NonNull VibrationEffect getSuccessVibrationEffect() { protected @Nullable VibrationEffect getSuccessVibrationEffect() {
if (!mCustomHaptics) { if (!mCustomHaptics) {
return super.getSuccessVibrationEffect(); return super.getSuccessVibrationEffect();
} }
@@ -274,7 +274,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
} }
@Override @Override
protected @NonNull VibrationEffect getErrorVibrationEffect() { protected @Nullable VibrationEffect getErrorVibrationEffect() {
if (!mCustomHaptics) { if (!mCustomHaptics) {
return super.getErrorVibrationEffect(); return super.getErrorVibrationEffect();
} }