Merge "Update configurable udfps haptics" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
270922b370
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -147,32 +149,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
|
||||
@@ -449,16 +441,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,7 +826,6 @@ public class UdfpsController implements DozeReceiver {
|
||||
mExecution.assertIsMainThread();
|
||||
mActivePointerId = -1;
|
||||
mGoodCaptureReceived = false;
|
||||
mMainHandler.removeCallbacks(mAcquiredVibration);
|
||||
if (mView == null) {
|
||||
Log.w(TAG, "Null view in onFingerUp");
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<StatusBarStateController.StateListener> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,25 +192,27 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,21 +344,29 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
||||
}
|
||||
|
||||
@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());
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull VibrationEffect getSuccessVibrationEffect() {
|
||||
protected @Nullable VibrationEffect getSuccessVibrationEffect() {
|
||||
if (!mCustomHaptics) {
|
||||
return super.getSuccessVibrationEffect();
|
||||
}
|
||||
@@ -274,7 +274,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull VibrationEffect getErrorVibrationEffect() {
|
||||
protected @Nullable VibrationEffect getErrorVibrationEffect() {
|
||||
if (!mCustomHaptics) {
|
||||
return super.getErrorVibrationEffect();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user