Merge changes from topic "sunfish-udfps"
* changes: Add anti burn-in logic for UdfpsView Update Fingerprint21, add Fingerprint21UdfpsMock
This commit is contained in:
@@ -25,4 +25,7 @@ oneway interface IUdfpsOverlayController {
|
||||
|
||||
// Hides the overlay.
|
||||
void hideUdfpsOverlay();
|
||||
|
||||
// Shows debug messages on the UDFPS overlay.
|
||||
void setDebugMessage(String message);
|
||||
}
|
||||
|
||||
@@ -4189,6 +4189,8 @@
|
||||
<string-array name="config_biometric_sensors" translatable="false" >
|
||||
<!-- <item>0:2:15</item> ID0:Fingerprint:Strong -->
|
||||
</string-array>
|
||||
<!--If true, allows the device to load udfps components on older HIDL implementations -->
|
||||
<bool name="allow_test_udfps" translatable="false" >false</bool>
|
||||
|
||||
<!-- Messages that should not be shown to the user during face auth enrollment. This should be
|
||||
used to hide messages that may be too chatty or messages that the user can't do much about.
|
||||
|
||||
@@ -2506,6 +2506,7 @@
|
||||
<java-symbol type="string" name="face_error_security_update_required" />
|
||||
|
||||
<java-symbol type="array" name="config_biometric_sensors" />
|
||||
<java-symbol type="bool" name="allow_test_udfps" />
|
||||
|
||||
<java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
|
||||
<java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
|
||||
|
||||
@@ -1036,6 +1036,11 @@
|
||||
<!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
|
||||
<dimen name="default_burn_in_prevention_offset">15dp</dimen>
|
||||
|
||||
<!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
|
||||
direction that elements aer moved to prevent burn-in on AOD-->
|
||||
<dimen name="udfps_burn_in_offset_x">8dp</dimen>
|
||||
<dimen name="udfps_burn_in_offset_y">8dp</dimen>
|
||||
|
||||
<dimen name="corner_size">8dp</dimen>
|
||||
<dimen name="top_padding">0dp</dimen>
|
||||
<dimen name="bottom_padding">48dp</dimen>
|
||||
|
||||
@@ -49,6 +49,8 @@ import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.doze.DozeReceiver;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
|
||||
import java.util.List;
|
||||
@@ -62,12 +64,13 @@ import javax.inject.Singleton;
|
||||
*/
|
||||
@Singleton
|
||||
public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
AuthDialogCallback {
|
||||
AuthDialogCallback, DozeReceiver {
|
||||
|
||||
private static final String TAG = "BiometricPrompt/AuthController";
|
||||
private static final String TAG = "AuthController";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private final CommandQueue mCommandQueue;
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
private final Injector mInjector;
|
||||
|
||||
// TODO: These should just be saved from onSaveState
|
||||
@@ -77,6 +80,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
|
||||
private Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private WindowManager mWindowManager;
|
||||
@Nullable
|
||||
private UdfpsController mUdfpsController;
|
||||
@VisibleForTesting
|
||||
IActivityTaskManager mActivityTaskManager;
|
||||
@@ -142,6 +146,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void dozeTimeTick() {
|
||||
if (mUdfpsController != null) {
|
||||
mUdfpsController.dozeTimeTick();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTryAgainPressed() {
|
||||
if (mReceiver == null) {
|
||||
@@ -251,14 +262,17 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
}
|
||||
|
||||
@Inject
|
||||
public AuthController(Context context, CommandQueue commandQueue) {
|
||||
this(context, commandQueue, new Injector());
|
||||
public AuthController(Context context, CommandQueue commandQueue,
|
||||
StatusBarStateController statusBarStateController) {
|
||||
this(context, commandQueue, statusBarStateController, new Injector());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AuthController(Context context, CommandQueue commandQueue, Injector injector) {
|
||||
AuthController(Context context, CommandQueue commandQueue,
|
||||
StatusBarStateController statusBarStateController, Injector injector) {
|
||||
super(context);
|
||||
mCommandQueue = commandQueue;
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mInjector = injector;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
@@ -280,7 +294,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
fpm.getSensorProperties();
|
||||
for (FingerprintSensorProperties props : fingerprintSensorProperties) {
|
||||
if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) {
|
||||
mUdfpsController = new UdfpsController(mContext);
|
||||
mUdfpsController = new UdfpsController(mContext, mStatusBarStateController);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -29,6 +30,7 @@ import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Spline;
|
||||
@@ -38,6 +40,8 @@ import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.BrightnessSynchronizer;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.doze.DozeReceiver;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
@@ -46,7 +50,7 @@ import java.io.IOException;
|
||||
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
|
||||
* and coordinates triggering of the high-brightness mode (HBM).
|
||||
*/
|
||||
class UdfpsController {
|
||||
class UdfpsController implements DozeReceiver {
|
||||
private static final String TAG = "UdfpsController";
|
||||
// Gamma approximation for the sRGB color space.
|
||||
private static final float DISPLAY_GAMMA = 2.2f;
|
||||
@@ -61,6 +65,7 @@ class UdfpsController {
|
||||
private final String mHbmPath;
|
||||
private final String mHbmEnableCommand;
|
||||
private final String mHbmDisableCommand;
|
||||
private final boolean mHbmSupported;
|
||||
// Brightness in nits in the high-brightness mode.
|
||||
private final float mHbmNits;
|
||||
// A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to a
|
||||
@@ -85,6 +90,11 @@ class UdfpsController {
|
||||
public void hideUdfpsOverlay() {
|
||||
UdfpsController.this.hideUdfpsOverlay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebugMessage(String message) {
|
||||
mView.setDebugMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@@ -116,7 +126,8 @@ class UdfpsController {
|
||||
}
|
||||
};
|
||||
|
||||
UdfpsController(Context context) {
|
||||
UdfpsController(@NonNull Context context,
|
||||
@NonNull StatusBarStateController statusBarStateController) {
|
||||
mFingerprintManager = context.getSystemService(FingerprintManager.class);
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
mContentResolver = context.getContentResolver();
|
||||
@@ -129,6 +140,10 @@ class UdfpsController {
|
||||
mHbmEnableCommand = context.getResources().getString(R.string.udfps_hbm_enable_command);
|
||||
mHbmDisableCommand = context.getResources().getString(R.string.udfps_hbm_disable_command);
|
||||
|
||||
mHbmSupported = !TextUtils.isEmpty(mHbmPath);
|
||||
mView.setHbmSupported(mHbmSupported);
|
||||
statusBarStateController.addCallback(mView);
|
||||
|
||||
// This range only consists of the minimum and maximum values, which only cover
|
||||
// non-high-brightness mode.
|
||||
float[] nitsRange = toFloatArray(context.getResources().obtainTypedArray(
|
||||
@@ -162,6 +177,11 @@ class UdfpsController {
|
||||
mIsOverlayShowing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dozeTimeTick() {
|
||||
mView.dozeTimeTick();
|
||||
}
|
||||
|
||||
private void showUdfpsOverlay() {
|
||||
mHandler.post(() -> {
|
||||
if (!mIsOverlayShowing) {
|
||||
@@ -224,9 +244,11 @@ class UdfpsController {
|
||||
mView.setScrimAlpha(computeScrimOpacity());
|
||||
mView.showScrimAndDot();
|
||||
try {
|
||||
FileWriter fw = new FileWriter(mHbmPath);
|
||||
fw.write(mHbmEnableCommand);
|
||||
fw.close();
|
||||
if (mHbmSupported) {
|
||||
FileWriter fw = new FileWriter(mHbmPath);
|
||||
fw.write(mHbmEnableCommand);
|
||||
fw.close();
|
||||
}
|
||||
mFingerprintManager.onFingerDown(x, y, minor, major);
|
||||
} catch (IOException e) {
|
||||
mView.hideScrimAndDot();
|
||||
@@ -238,13 +260,15 @@ class UdfpsController {
|
||||
mFingerprintManager.onFingerUp();
|
||||
// Hiding the scrim before disabling HBM results in less noticeable flicker.
|
||||
mView.hideScrimAndDot();
|
||||
try {
|
||||
FileWriter fw = new FileWriter(mHbmPath);
|
||||
fw.write(mHbmDisableCommand);
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
mView.showScrimAndDot();
|
||||
Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
|
||||
if (mHbmSupported) {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(mHbmPath);
|
||||
fw.write(mHbmDisableCommand);
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
mView.showScrimAndDot();
|
||||
Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
@@ -23,25 +25,33 @@ import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.doze.DozeReceiver;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
|
||||
/**
|
||||
* A full screen view with a configurable illumination dot and scrim.
|
||||
*/
|
||||
public class UdfpsView extends View {
|
||||
public class UdfpsView extends View implements DozeReceiver,
|
||||
StatusBarStateController.StateListener {
|
||||
private static final String TAG = "UdfpsView";
|
||||
|
||||
// Values in pixels.
|
||||
private static final float SENSOR_SHADOW_OFFSET = 2.0f;
|
||||
private static final float SENSOR_SHADOW_RADIUS = 2.0f;
|
||||
private static final float SENSOR_OUTLINE_WIDTH = 2.0f;
|
||||
|
||||
private static final int DEBUG_TEXT_SIZE_PX = 32;
|
||||
|
||||
private final Rect mScrimRect;
|
||||
private final Paint mScrimPaint;
|
||||
private final Paint mDebugTextPaint;
|
||||
|
||||
private float mSensorX;
|
||||
private float mSensorY;
|
||||
@@ -50,11 +60,20 @@ public class UdfpsView extends View {
|
||||
private final float mSensorRadius;
|
||||
private final float mSensorMarginBottom;
|
||||
private final float mSensorTouchAreaCoefficient;
|
||||
private final int mMaxBurnInOffsetX;
|
||||
private final int mMaxBurnInOffsetY;
|
||||
|
||||
private final Rect mTouchableRegion;
|
||||
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener;
|
||||
|
||||
// AOD anti-burn-in offsets
|
||||
private float mInterpolatedDarkAmount;
|
||||
private float mBurnInOffsetX;
|
||||
private float mBurnInOffsetY;
|
||||
|
||||
private boolean mIsScrimShowing;
|
||||
private boolean mHbmSupported;
|
||||
private String mDebugMessage;
|
||||
|
||||
public UdfpsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -80,18 +99,29 @@ public class UdfpsView extends View {
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
mMaxBurnInOffsetX = getResources()
|
||||
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
|
||||
mMaxBurnInOffsetY = getResources()
|
||||
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
|
||||
|
||||
mScrimRect = new Rect();
|
||||
mScrimPaint = new Paint(0 /* flags */);
|
||||
mScrimPaint.setColor(Color.BLACK);
|
||||
|
||||
mSensorRect = new RectF();
|
||||
mSensorPaint = new Paint(0 /* flags */);
|
||||
mSensorPaint.setAntiAlias(true);
|
||||
mSensorPaint.setColor(Color.WHITE);
|
||||
mSensorPaint.setStyle(Paint.Style.STROKE);
|
||||
mSensorPaint.setStrokeWidth(SENSOR_OUTLINE_WIDTH);
|
||||
mSensorPaint.setShadowLayer(SENSOR_OUTLINE_WIDTH, 0, 0, Color.BLACK);
|
||||
mSensorPaint.setShadowLayer(SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK);
|
||||
mSensorPaint.setAntiAlias(true);
|
||||
|
||||
mDebugTextPaint = new Paint();
|
||||
mDebugTextPaint.setAntiAlias(true);
|
||||
mDebugTextPaint.setColor(Color.BLUE);
|
||||
mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX);
|
||||
|
||||
mTouchableRegion = new Rect();
|
||||
mInsetsListener = internalInsetsInfo -> {
|
||||
internalInsetsInfo.setTouchableInsets(
|
||||
@@ -102,6 +132,29 @@ public class UdfpsView extends View {
|
||||
mIsScrimShowing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dozeTimeTick() {
|
||||
updateAodPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDozeAmountChanged(float linear, float eased) {
|
||||
mInterpolatedDarkAmount = eased;
|
||||
updateAodPosition();
|
||||
}
|
||||
|
||||
private void updateAodPosition() {
|
||||
mBurnInOffsetX = MathUtils.lerp(0f,
|
||||
getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
|
||||
- mMaxBurnInOffsetX,
|
||||
mInterpolatedDarkAmount);
|
||||
mBurnInOffsetY = MathUtils.lerp(0f,
|
||||
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
|
||||
- 0.5f * mMaxBurnInOffsetY,
|
||||
mInterpolatedDarkAmount);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
@@ -131,10 +184,29 @@ public class UdfpsView extends View {
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (mIsScrimShowing) {
|
||||
|
||||
if (mIsScrimShowing && mHbmSupported) {
|
||||
// Only draw the scrim if HBM is supported.
|
||||
canvas.drawRect(mScrimRect, mScrimPaint);
|
||||
}
|
||||
|
||||
// Translation should affect everything but the scrim.
|
||||
canvas.save();
|
||||
canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
|
||||
if (!TextUtils.isEmpty(mDebugMessage)) {
|
||||
canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint);
|
||||
}
|
||||
canvas.drawOval(mSensorRect, mSensorPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
void setHbmSupported(boolean hbmSupported) {
|
||||
mHbmSupported = hbmSupported;
|
||||
}
|
||||
|
||||
void setDebugMessage(String message) {
|
||||
mDebugMessage = message;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
boolean isValidTouch(float x, float y, float pressure) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.view.View;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.assist.AssistManager;
|
||||
import com.android.systemui.biometrics.AuthController;
|
||||
import com.android.systemui.doze.DozeHost;
|
||||
import com.android.systemui.doze.DozeLog;
|
||||
import com.android.systemui.doze.DozeReceiver;
|
||||
@@ -88,6 +89,7 @@ public final class DozeServiceHost implements DozeHost {
|
||||
private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
|
||||
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
|
||||
private final LockscreenLockIconController mLockscreenLockIconController;
|
||||
private final AuthController mAuthController;
|
||||
private NotificationIconAreaController mNotificationIconAreaController;
|
||||
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
private NotificationPanelViewController mNotificationPanel;
|
||||
@@ -110,7 +112,8 @@ public final class DozeServiceHost implements DozeHost {
|
||||
PulseExpansionHandler pulseExpansionHandler,
|
||||
NotificationShadeWindowController notificationShadeWindowController,
|
||||
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
|
||||
LockscreenLockIconController lockscreenLockIconController) {
|
||||
LockscreenLockIconController lockscreenLockIconController,
|
||||
AuthController authController) {
|
||||
super();
|
||||
mDozeLog = dozeLog;
|
||||
mPowerManager = powerManager;
|
||||
@@ -130,6 +133,7 @@ public final class DozeServiceHost implements DozeHost {
|
||||
mNotificationShadeWindowController = notificationShadeWindowController;
|
||||
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
|
||||
mLockscreenLockIconController = lockscreenLockIconController;
|
||||
mAuthController = authController;
|
||||
}
|
||||
|
||||
// TODO: we should try to not pass status bar in here if we can avoid it.
|
||||
@@ -297,6 +301,7 @@ public final class DozeServiceHost implements DozeHost {
|
||||
@Override
|
||||
public void dozeTimeTick() {
|
||||
mNotificationPanel.dozeTimeTick();
|
||||
mAuthController.dozeTimeTick();
|
||||
if (mAmbientIndicationContainer instanceof DozeReceiver) {
|
||||
((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -103,8 +104,8 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
|
||||
when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
|
||||
|
||||
mAuthController = new TestableAuthController(
|
||||
context, mock(CommandQueue.class), new MockInjector());
|
||||
mAuthController = new TestableAuthController(context, mock(CommandQueue.class),
|
||||
mock(StatusBarStateController.class), new MockInjector());
|
||||
|
||||
mAuthController.start();
|
||||
}
|
||||
@@ -502,8 +503,9 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
private int mBuildCount = 0;
|
||||
private PromptInfo mLastBiometricPromptInfo;
|
||||
|
||||
TestableAuthController(Context context, CommandQueue commandQueue, Injector injector) {
|
||||
super(context, commandQueue, injector);
|
||||
TestableAuthController(Context context, CommandQueue commandQueue,
|
||||
StatusBarStateController statusBarStateController, Injector injector) {
|
||||
super(context, commandQueue, statusBarStateController, injector);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.assist.AssistManager;
|
||||
import com.android.systemui.biometrics.AuthController;
|
||||
import com.android.systemui.doze.DozeHost;
|
||||
import com.android.systemui.doze.DozeLog;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
@@ -90,6 +91,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
|
||||
@Mock private View mAmbientIndicationContainer;
|
||||
@Mock private BiometricUnlockController mBiometricUnlockController;
|
||||
@Mock private LockscreenLockIconController mLockscreenLockIconController;
|
||||
@Mock private AuthController mAuthController;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -100,7 +102,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
|
||||
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
|
||||
mKeyguardUpdateMonitor, mVisualStabilityManager, mPulseExpansionHandler,
|
||||
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
|
||||
mLockscreenLockIconController);
|
||||
mLockscreenLockIconController, mAuthController);
|
||||
|
||||
mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController,
|
||||
mStatusBarKeyguardViewManager, mNotificationShadeWindowViewController,
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
|
||||
private final PowerManager mPowerManager;
|
||||
private final VibrationEffect mSuccessVibrationEffect;
|
||||
private final VibrationEffect mErrorVibrationEffect;
|
||||
private boolean mShouldSendErrorToClient;
|
||||
private boolean mShouldSendErrorToClient = true;
|
||||
private boolean mAlreadyCancelled;
|
||||
|
||||
/**
|
||||
@@ -85,11 +85,11 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
|
||||
// case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
|
||||
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
|
||||
// sends errors once per ClientMonitor.
|
||||
if (!mShouldSendErrorToClient) {
|
||||
if (mShouldSendErrorToClient) {
|
||||
logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
|
||||
try {
|
||||
if (getListener() != null) {
|
||||
mShouldSendErrorToClient = true;
|
||||
mShouldSendErrorToClient = false;
|
||||
getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -99,6 +99,14 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
||||
return getCookie() != 0;
|
||||
}
|
||||
|
||||
public long getOperationId() {
|
||||
return mOperationId;
|
||||
}
|
||||
|
||||
public boolean isRestricted() {
|
||||
return mIsRestricted;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCryptoOperation() {
|
||||
return mOperationId != 0;
|
||||
|
||||
@@ -175,7 +175,7 @@ public class BiometricScheduler {
|
||||
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
|
||||
@NonNull private final IBiometricService mBiometricService;
|
||||
@NonNull private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
@NonNull protected final InternalCallback mInternalCallback;
|
||||
@NonNull private final InternalCallback mInternalCallback;
|
||||
@NonNull private final Queue<Operation> mPendingOperations;
|
||||
@Nullable private Operation mCurrentOperation;
|
||||
@NonNull private final ArrayDeque<CrashState> mCrashStates;
|
||||
@@ -185,7 +185,15 @@ public class BiometricScheduler {
|
||||
// starting the next client).
|
||||
public class InternalCallback implements ClientMonitor.Callback {
|
||||
@Override
|
||||
public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
|
||||
public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {
|
||||
Slog.d(getTag(), "[Started] " + clientMonitor);
|
||||
if (mCurrentOperation.mClientCallback != null) {
|
||||
mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
|
||||
mHandler.post(() -> {
|
||||
if (mCurrentOperation == null) {
|
||||
Slog.e(getTag(), "[Finishing] " + clientMonitor
|
||||
@@ -235,6 +243,14 @@ public class BiometricScheduler {
|
||||
mCrashStates = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A reference to the internal callback that should be invoked whenever the scheduler
|
||||
* needs to (e.g. client started, client finished).
|
||||
*/
|
||||
@NonNull protected InternalCallback getInternalCallback() {
|
||||
return mInternalCallback;
|
||||
}
|
||||
|
||||
private String getTag() {
|
||||
return BASE_TAG + "/" + mBiometricTag;
|
||||
}
|
||||
@@ -262,7 +278,7 @@ public class BiometricScheduler {
|
||||
}
|
||||
|
||||
final Interruptable interruptable = (Interruptable) currentClient;
|
||||
interruptable.cancelWithoutStarting(mInternalCallback);
|
||||
interruptable.cancelWithoutStarting(getInternalCallback());
|
||||
// Now we wait for the client to send its FinishCallback, which kicks off the next
|
||||
// operation.
|
||||
return;
|
||||
@@ -280,10 +296,7 @@ public class BiometricScheduler {
|
||||
final boolean shouldStartNow = currentClient.getCookie() == 0;
|
||||
if (shouldStartNow) {
|
||||
Slog.d(getTag(), "[Starting] " + mCurrentOperation);
|
||||
if (mCurrentOperation.mClientCallback != null) {
|
||||
mCurrentOperation.mClientCallback.onClientStarted(currentClient);
|
||||
}
|
||||
currentClient.start(mInternalCallback);
|
||||
currentClient.start(getInternalCallback());
|
||||
mCurrentOperation.state = Operation.STATE_STARTED;
|
||||
} else {
|
||||
try {
|
||||
@@ -326,11 +339,8 @@ public class BiometricScheduler {
|
||||
}
|
||||
|
||||
Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
|
||||
if (mCurrentOperation.mClientCallback != null) {
|
||||
mCurrentOperation.mClientCallback.onClientStarted(mCurrentOperation.clientMonitor);
|
||||
}
|
||||
mCurrentOperation.state = Operation.STATE_STARTED;
|
||||
mCurrentOperation.clientMonitor.start(mInternalCallback);
|
||||
mCurrentOperation.clientMonitor.start(getInternalCallback());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -144,6 +144,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
|
||||
*/
|
||||
public void start(@NonNull Callback callback) {
|
||||
mCallback = callback;
|
||||
mCallback.onClientStarted(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,6 +58,10 @@ public abstract class LoggableMonitor {
|
||||
mStatsClient = statsClient;
|
||||
}
|
||||
|
||||
public int getStatsClient() {
|
||||
return mStatsClient;
|
||||
}
|
||||
|
||||
private boolean isAnyFieldUnknown() {
|
||||
return mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
|
||||
|| mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN
|
||||
|
||||
@@ -84,7 +84,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
private static final String TAG = "Fingerprint21";
|
||||
private static final int ENROLL_TIMEOUT_SEC = 60;
|
||||
|
||||
private final Context mContext;
|
||||
final Context mContext;
|
||||
private final IActivityTaskManager mActivityTaskManager;
|
||||
private final FingerprintSensorProperties mSensorProperties;
|
||||
private final BiometricScheduler mScheduler;
|
||||
@@ -96,6 +96,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
private final Map<Integer, Long> mAuthenticatorIds;
|
||||
|
||||
@Nullable private IBiometricsFingerprint mDaemon;
|
||||
@NonNull private final HalResultController mHalResultController;
|
||||
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
|
||||
private int mCurrentUserId = UserHandle.USER_NULL;
|
||||
|
||||
@@ -146,15 +147,37 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
}
|
||||
};
|
||||
|
||||
private final IBiometricsFingerprintClientCallback mDaemonCallback =
|
||||
new IBiometricsFingerprintClientCallback.Stub() {
|
||||
public static class HalResultController extends IBiometricsFingerprintClientCallback.Stub {
|
||||
|
||||
/**
|
||||
* Interface to sends results to the HalResultController's owner.
|
||||
*/
|
||||
public interface Callback {
|
||||
/**
|
||||
* Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
|
||||
*/
|
||||
void onHardwareUnavailable();
|
||||
}
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
@NonNull final Handler mHandler;
|
||||
@NonNull final BiometricScheduler mScheduler;
|
||||
@Nullable private Callback mCallback;
|
||||
|
||||
HalResultController(@NonNull Context context, @NonNull Handler handler,
|
||||
@NonNull BiometricScheduler scheduler) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
mScheduler = scheduler;
|
||||
}
|
||||
|
||||
public void setCallback(@Nullable Callback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
|
||||
mHandler.post(() -> {
|
||||
final CharSequence name = FingerprintUtils.getInstance()
|
||||
.getUniqueName(mContext, mCurrentUserId);
|
||||
final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
|
||||
|
||||
final ClientMonitor<?> client = mScheduler.getCurrentClient();
|
||||
if (!(client instanceof FingerprintEnrollClient)) {
|
||||
Slog.e(TAG, "onEnrollResult for non-enroll client: "
|
||||
@@ -162,6 +185,11 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
return;
|
||||
}
|
||||
|
||||
final int currentUserId = client.getTargetUserId();
|
||||
final CharSequence name = FingerprintUtils.getInstance()
|
||||
.getUniqueName(mContext, currentUserId);
|
||||
final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
|
||||
|
||||
final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
|
||||
enrollClient.onEnrollResult(fingerprint, remaining);
|
||||
});
|
||||
@@ -224,8 +252,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
|
||||
if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
|
||||
Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
|
||||
mDaemon = null;
|
||||
mCurrentUserId = UserHandle.USER_NULL;
|
||||
if (mCallback != null) {
|
||||
mCallback.onHardwareUnavailable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -262,20 +291,27 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
enumerateConsumer.onEnumerationResult(fp, remaining);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Fingerprint21(@NonNull Context context, int sensorId,
|
||||
Fingerprint21(@NonNull Context context, @NonNull BiometricScheduler scheduler,
|
||||
@NonNull Handler handler, int sensorId,
|
||||
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
|
||||
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
@NonNull HalResultController controller) {
|
||||
mContext = context;
|
||||
mScheduler = scheduler;
|
||||
mHandler = handler;
|
||||
mActivityTaskManager = ActivityTaskManager.getService();
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mTaskStackListener = new BiometricTaskStackListener();
|
||||
mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
|
||||
mLazyDaemon = Fingerprint21.this::getDaemon;
|
||||
mLockoutResetDispatcher = lockoutResetDispatcher;
|
||||
mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
|
||||
mScheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
|
||||
mHalResultController = controller;
|
||||
mHalResultController.setCallback(() -> {
|
||||
mDaemon = null;
|
||||
mCurrentUserId = UserHandle.USER_NULL;
|
||||
});
|
||||
|
||||
try {
|
||||
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
|
||||
@@ -303,6 +339,17 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType);
|
||||
}
|
||||
|
||||
static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
|
||||
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
|
||||
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
final BiometricScheduler scheduler =
|
||||
new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
|
||||
final HalResultController controller = new HalResultController(context, handler, scheduler);
|
||||
return new Fingerprint21(context, scheduler, handler, sensorId, lockoutResetDispatcher,
|
||||
controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serviceDied(long cookie) {
|
||||
Slog.e(TAG, "HAL died");
|
||||
@@ -355,7 +402,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
// successfully set.
|
||||
long halId = 0;
|
||||
try {
|
||||
halId = mDaemon.setNotify(mDaemonCallback);
|
||||
halId = mDaemon.setNotify(mHalResultController);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to set callback for fingerprint HAL", e);
|
||||
mDaemon = null;
|
||||
@@ -373,6 +420,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
return mDaemon;
|
||||
}
|
||||
|
||||
@Nullable IUdfpsOverlayController getUdfpsOverlayController() {
|
||||
return mUdfpsOverlayController;
|
||||
}
|
||||
@LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
|
||||
return mLockoutTracker.getLockoutModeForUser(userId);
|
||||
}
|
||||
@@ -492,7 +542,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
|
||||
void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
|
||||
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
|
||||
@Nullable Surface surface, boolean restricted, int statsClient) {
|
||||
boolean restricted, int statsClient, boolean isKeyguard) {
|
||||
mHandler.post(() -> {
|
||||
scheduleUpdateActiveUserWithoutHandler(userId);
|
||||
|
||||
@@ -500,8 +550,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
|
||||
mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
|
||||
opPackageName, cookie, false /* requireConfirmation */,
|
||||
mSensorProperties.sensorId, isStrongBiometric, surface, statsClient,
|
||||
mTaskStackListener, mLockoutTracker, mUdfpsOverlayController);
|
||||
mSensorProperties.sensorId, isStrongBiometric, statsClient,
|
||||
mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, isKeyguard);
|
||||
mScheduler.scheduleClientMonitor(client);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors.fingerprint;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
|
||||
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.hardware.fingerprint.IUdfpsOverlayController;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import com.android.server.biometrics.sensors.AuthenticationConsumer;
|
||||
import com.android.server.biometrics.sensors.BiometricScheduler;
|
||||
import com.android.server.biometrics.sensors.ClientMonitor;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
|
||||
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* A mockable/testable provider of the {@link android.hardware.biometrics.fingerprint.V2_3} HIDL
|
||||
* interface. This class is intended simulate UDFPS logic for devices that do not have an actual
|
||||
* fingerprint@2.3 HAL (where UDFPS starts to become supported)
|
||||
*
|
||||
* UDFPS "accept" can only happen within a set amount of time after a sensor authentication. This is
|
||||
* specified by {@link MockHalResultController#AUTH_VALIDITY_MS}. Touches after this duration will
|
||||
* be treated as "reject".
|
||||
*
|
||||
* This class provides framework logic to emulate, for testing only, the UDFPS functionalies below:
|
||||
*
|
||||
* 1) IF either A) the caller is keyguard, and the device is not in a trusted state (authenticated
|
||||
* via biometric sensor or unlocked with a trust agent {@see android.app.trust.TrustManager}, OR
|
||||
* B) the caller is not keyguard, and regardless of trusted state, AND (following applies to both
|
||||
* (A) and (B) above) {@link FingerprintManager#onFingerDown(int, int, float, float)} is
|
||||
* received, this class will respond with {@link AuthenticationCallback#onAuthenticationFailed()}
|
||||
* after a tunable flat_time + variance_time.
|
||||
*
|
||||
* In the case above (1), callers must not receive a successful authentication event here because
|
||||
* the sensor has not actually been authenticated.
|
||||
*
|
||||
* 2) IF A) the caller is keyguard and the device is not in a trusted state, OR B) the caller is not
|
||||
* keyguard and regardless of trusted state, AND (following applies to both (A) and (B)) the
|
||||
* sensor is touched and the fingerprint is accepted by the HAL, and then
|
||||
* {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
|
||||
* respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
|
||||
* after a tunable flat_time + variance_time. Note that the authentication callback from the
|
||||
* sensor is held until {@link FingerprintManager#onFingerDown(int, int, float, float)} is
|
||||
* invoked.
|
||||
*
|
||||
* In the case above (2), callers can receive a successful authentication callback because the
|
||||
* real sensor was authenticated. Note that even though the real sensor was touched, keyguard
|
||||
* fingerprint authentication does not put keyguard into a trusted state because the
|
||||
* authentication callback is held until onFingerDown was invoked. This allows callers such as
|
||||
* keyguard to simulate a realistic path.
|
||||
*
|
||||
* 3) IF the caller is keyguard AND the device in a trusted state and then
|
||||
* {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
|
||||
* respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
|
||||
* after a tunable flat_time + variance time.
|
||||
*
|
||||
* In the case above (3), since the device is already unlockable via trust agent, it's fine to
|
||||
* simulate the successful auth path. Non-keyguard clients will fall into either (1) or (2)
|
||||
* above.
|
||||
*
|
||||
* This class currently does not simulate false rejection. Conversely, this class relies on the
|
||||
* real hardware sensor so does not affect false acceptance.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManager.TrustListener {
|
||||
|
||||
private static final String TAG = "Fingerprint21UdfpsMock";
|
||||
|
||||
// Secure setting integer. If true, the system will load this class to enable udfps testing.
|
||||
public static final String CONFIG_ENABLE_TEST_UDFPS =
|
||||
"com.android.server.biometrics.sensors.fingerprint.test_udfps.enable";
|
||||
// Secure setting integer. A fixed duration intended to simulate something like the duration
|
||||
// required for image capture.
|
||||
private static final String CONFIG_AUTH_DELAY_PT1 =
|
||||
"com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt1";
|
||||
// Secure setting integer. A fixed duration intended to simulate something like the duration
|
||||
// required for template matching to complete.
|
||||
private static final String CONFIG_AUTH_DELAY_PT2 =
|
||||
"com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt2";
|
||||
// Secure setting integer. A random value between [-randomness, randomness] will be added to the
|
||||
// capture delay above for each accept/reject.
|
||||
private static final String CONFIG_AUTH_DELAY_RANDOMNESS =
|
||||
"com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_randomness";
|
||||
|
||||
private static final int DEFAULT_AUTH_DELAY_PT1_MS = 300;
|
||||
private static final int DEFAULT_AUTH_DELAY_PT2_MS = 400;
|
||||
private static final int DEFAULT_AUTH_DELAY_RANDOMNESS_MS = 100;
|
||||
|
||||
@NonNull private final TestableBiometricScheduler mScheduler;
|
||||
@NonNull private final Handler mHandler;
|
||||
@NonNull private final FingerprintSensorProperties mSensorProperties;
|
||||
@NonNull private final MockHalResultController mMockHalResultController;
|
||||
@NonNull private final TrustManager mTrustManager;
|
||||
@NonNull private final SparseBooleanArray mUserHasTrust;
|
||||
@NonNull private final Random mRandom;
|
||||
@NonNull private final FakeRejectRunnable mFakeRejectRunnable;
|
||||
@NonNull private final FakeAcceptRunnable mFakeAcceptRunnable;
|
||||
@NonNull private final RestartAuthRunnable mRestartAuthRunnable;
|
||||
|
||||
private static class TestableBiometricScheduler extends BiometricScheduler {
|
||||
@NonNull private final TestableInternalCallback mInternalCallback;
|
||||
@NonNull private Fingerprint21UdfpsMock mFingerprint21;
|
||||
|
||||
TestableBiometricScheduler(@NonNull String tag,
|
||||
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
super(tag, gestureAvailabilityDispatcher);
|
||||
mInternalCallback = new TestableInternalCallback();
|
||||
}
|
||||
|
||||
class TestableInternalCallback extends InternalCallback {
|
||||
@Override
|
||||
public void onClientStarted(ClientMonitor<?> clientMonitor) {
|
||||
super.onClientStarted(clientMonitor);
|
||||
Slog.d(TAG, "Client started: " + clientMonitor);
|
||||
mFingerprint21.setDebugMessage("Started: " + clientMonitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
|
||||
super.onClientFinished(clientMonitor, success);
|
||||
Slog.d(TAG, "Client finished: " + clientMonitor);
|
||||
mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
|
||||
mFingerprint21 = fingerprint21;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the internal finish callback so it can be used for testing
|
||||
*/
|
||||
@Override
|
||||
@NonNull protected InternalCallback getInternalCallback() {
|
||||
return mInternalCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All of the mocking/testing should happen in here. This way we don't need to modify the
|
||||
* {@link com.android.server.biometrics.sensors.ClientMonitor} implementations and can run the
|
||||
* real path there.
|
||||
*/
|
||||
private static class MockHalResultController extends HalResultController {
|
||||
|
||||
// Duration for which a sensor authentication can be treated as UDFPS success.
|
||||
private static final int AUTH_VALIDITY_MS = 10 * 1000; // 10 seconds
|
||||
|
||||
static class LastAuthArgs {
|
||||
@NonNull final AuthenticationConsumer lastAuthenticatedClient;
|
||||
final long deviceId;
|
||||
final int fingerId;
|
||||
final int groupId;
|
||||
@Nullable final ArrayList<Byte> token;
|
||||
|
||||
LastAuthArgs(@NonNull AuthenticationConsumer authenticationConsumer, long deviceId,
|
||||
int fingerId, int groupId, @Nullable ArrayList<Byte> token) {
|
||||
lastAuthenticatedClient = authenticationConsumer;
|
||||
this.deviceId = deviceId;
|
||||
this.fingerId = fingerId;
|
||||
this.groupId = groupId;
|
||||
if (token == null) {
|
||||
this.token = null;
|
||||
} else {
|
||||
// Store a copy so the owner can be GC'd
|
||||
this.token = new ArrayList<>(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialized after the constructor, but before it's ever used.
|
||||
@NonNull private RestartAuthRunnable mRestartAuthRunnable;
|
||||
@NonNull private Fingerprint21UdfpsMock mFingerprint21;
|
||||
@Nullable private LastAuthArgs mLastAuthArgs;
|
||||
|
||||
MockHalResultController(@NonNull Context context, @NonNull Handler handler,
|
||||
@NonNull BiometricScheduler scheduler) {
|
||||
super(context, handler, scheduler);
|
||||
}
|
||||
|
||||
void init(@NonNull RestartAuthRunnable restartAuthRunnable,
|
||||
@NonNull Fingerprint21UdfpsMock fingerprint21) {
|
||||
mRestartAuthRunnable = restartAuthRunnable;
|
||||
mFingerprint21 = fingerprint21;
|
||||
}
|
||||
|
||||
@Nullable AuthenticationConsumer getLastAuthenticatedClient() {
|
||||
return mLastAuthArgs != null ? mLastAuthArgs.lastAuthenticatedClient : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts the HAL authentication and holds it until the UDFPS simulation decides
|
||||
* that authentication finished.
|
||||
*/
|
||||
@Override
|
||||
public void onAuthenticated(long deviceId, int fingerId, int groupId,
|
||||
ArrayList<Byte> token) {
|
||||
mHandler.post(() -> {
|
||||
final ClientMonitor<?> client = mScheduler.getCurrentClient();
|
||||
if (!(client instanceof AuthenticationConsumer)) {
|
||||
Slog.e(TAG, "Non authentication consumer: " + client);
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean accepted = fingerId != 0;
|
||||
if (accepted) {
|
||||
mFingerprint21.setDebugMessage("Finger accepted");
|
||||
} else {
|
||||
mFingerprint21.setDebugMessage("Finger rejected");
|
||||
}
|
||||
|
||||
final AuthenticationConsumer authenticationConsumer =
|
||||
(AuthenticationConsumer) client;
|
||||
mLastAuthArgs = new LastAuthArgs(authenticationConsumer, deviceId, fingerId,
|
||||
groupId, token);
|
||||
|
||||
// Remove any existing restart runnbables since auth just started, and put a new
|
||||
// one on the queue.
|
||||
mHandler.removeCallbacks(mRestartAuthRunnable);
|
||||
mRestartAuthRunnable.setLastAuthReference(authenticationConsumer);
|
||||
mHandler.postDelayed(mRestartAuthRunnable, AUTH_VALIDITY_MS);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls through to the real interface and notifies clients of accept/reject.
|
||||
*/
|
||||
void sendAuthenticated(long deviceId, int fingerId, int groupId,
|
||||
ArrayList<Byte> token) {
|
||||
Slog.d(TAG, "sendAuthenticated: " + (fingerId != 0));
|
||||
mFingerprint21.setDebugMessage("Udfps match: " + (fingerId != 0));
|
||||
super.onAuthenticated(deviceId, fingerId, groupId, token);
|
||||
}
|
||||
}
|
||||
|
||||
static Fingerprint21UdfpsMock newInstance(@NonNull Context context, int sensorId,
|
||||
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
|
||||
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
Slog.d(TAG, "Creating Fingerprint23Mock!");
|
||||
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
final TestableBiometricScheduler scheduler =
|
||||
new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
|
||||
final MockHalResultController controller =
|
||||
new MockHalResultController(context, handler, scheduler);
|
||||
return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId,
|
||||
lockoutResetDispatcher, controller);
|
||||
}
|
||||
|
||||
private static abstract class FakeFingerRunnable implements Runnable {
|
||||
private long mFingerDownTime;
|
||||
private int mCaptureDuration;
|
||||
|
||||
/**
|
||||
* @param fingerDownTime System time when onFingerDown occurred
|
||||
* @param captureDuration Duration that the finger needs to be down for
|
||||
*/
|
||||
void setSimulationTime(long fingerDownTime, int captureDuration) {
|
||||
mFingerDownTime = fingerDownTime;
|
||||
mCaptureDuration = captureDuration;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isImageCaptureComplete() {
|
||||
return System.currentTimeMillis() - mFingerDownTime > mCaptureDuration;
|
||||
}
|
||||
}
|
||||
|
||||
private final class FakeRejectRunnable extends FakeFingerRunnable {
|
||||
@Override
|
||||
public void run() {
|
||||
mMockHalResultController.sendAuthenticated(0, 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
private final class FakeAcceptRunnable extends FakeFingerRunnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mMockHalResultController.mLastAuthArgs == null) {
|
||||
// This can happen if the user has trust agents enabled, which make lockscreen
|
||||
// dismissable. Send a fake non-zero (accept) finger.
|
||||
Slog.d(TAG, "Sending fake finger");
|
||||
mMockHalResultController.sendAuthenticated(1 /* deviceId */,
|
||||
1 /* fingerId */, 1 /* groupId */, null /* token */);
|
||||
} else {
|
||||
mMockHalResultController.sendAuthenticated(
|
||||
mMockHalResultController.mLastAuthArgs.deviceId,
|
||||
mMockHalResultController.mLastAuthArgs.fingerId,
|
||||
mMockHalResultController.mLastAuthArgs.groupId,
|
||||
mMockHalResultController.mLastAuthArgs.token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The fingerprint HAL allows multiple (5) fingerprint attempts per HIDL invocation of the
|
||||
* authenticate method. However, valid fingerprint authentications are invalidated after
|
||||
* {@link MockHalResultController#AUTH_VALIDITY_MS}, meaning UDFPS touches will be reported as
|
||||
* rejects if touched after that duration. However, since a valid fingerprint was detected, the
|
||||
* HAL and FingerprintService will not look for subsequent fingerprints.
|
||||
*
|
||||
* In order to keep the FingerprintManager API consistent (that multiple fingerprint attempts
|
||||
* are allowed per auth lifecycle), we internally cancel and restart authentication so that the
|
||||
* sensor is responsive again.
|
||||
*/
|
||||
private static final class RestartAuthRunnable implements Runnable {
|
||||
@NonNull private final Fingerprint21UdfpsMock mFingerprint21;
|
||||
@NonNull private final TestableBiometricScheduler mScheduler;
|
||||
|
||||
// Store a reference to the auth consumer that should be invalidated.
|
||||
private AuthenticationConsumer mLastAuthConsumer;
|
||||
|
||||
RestartAuthRunnable(@NonNull Fingerprint21UdfpsMock fingerprint21,
|
||||
@NonNull TestableBiometricScheduler scheduler) {
|
||||
mFingerprint21 = fingerprint21;
|
||||
mScheduler = scheduler;
|
||||
}
|
||||
|
||||
void setLastAuthReference(AuthenticationConsumer lastAuthConsumer) {
|
||||
mLastAuthConsumer = lastAuthConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final ClientMonitor<?> client = mScheduler.getCurrentClient();
|
||||
|
||||
// We don't care about FingerprintDetectClient, since accept/rejects are both OK. UDFPS
|
||||
// rejects will just simulate the path where non-enrolled fingers are presented.
|
||||
if (!(client instanceof FingerprintAuthenticationClient)) {
|
||||
Slog.e(TAG, "Non-FingerprintAuthenticationClient client: " + client);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perhaps the runnable is stale, or the user stopped/started auth manually. Do not
|
||||
// restart auth in this case.
|
||||
if (client != mLastAuthConsumer) {
|
||||
Slog.e(TAG, "Current client: " + client
|
||||
+ " does not match mLastAuthConsumer: " + mLastAuthConsumer);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.d(TAG, "Restarting auth, current: " + client);
|
||||
mFingerprint21.setDebugMessage("Auth timed out");
|
||||
|
||||
final FingerprintAuthenticationClient authClient =
|
||||
(FingerprintAuthenticationClient) client;
|
||||
// Store the authClient parameters so it can be rescheduled
|
||||
final IBinder token = client.getToken();
|
||||
final long operationId = authClient.getOperationId();
|
||||
final int user = client.getTargetUserId();
|
||||
final int cookie = client.getCookie();
|
||||
final ClientMonitorCallbackConverter listener = client.getListener();
|
||||
final String opPackageName = client.getOwnerString();
|
||||
final boolean restricted = authClient.isRestricted();
|
||||
final int statsClient = client.getStatsClient();
|
||||
final boolean isKeyguard = authClient.isKeyguard();
|
||||
|
||||
// Don't actually send cancel() to the HAL, since successful auth already finishes
|
||||
// HAL authenticate() lifecycle. Just
|
||||
mScheduler.getInternalCallback().onClientFinished(client, true /* success */);
|
||||
|
||||
// Schedule this only after we invoke onClientFinished for the previous client, so that
|
||||
// internal preemption logic is not run.
|
||||
mFingerprint21.scheduleAuthenticate(token, operationId, user, cookie,
|
||||
listener, opPackageName, restricted, statsClient, isKeyguard);
|
||||
}
|
||||
}
|
||||
|
||||
private Fingerprint21UdfpsMock(@NonNull Context context,
|
||||
@NonNull TestableBiometricScheduler scheduler,
|
||||
@NonNull Handler handler, int sensorId,
|
||||
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
|
||||
@NonNull MockHalResultController controller) {
|
||||
super(context, scheduler, handler, sensorId, lockoutResetDispatcher, controller);
|
||||
mScheduler = scheduler;
|
||||
mScheduler.init(this);
|
||||
mHandler = handler;
|
||||
mSensorProperties = new FingerprintSensorProperties(sensorId,
|
||||
FingerprintSensorProperties.TYPE_UDFPS);
|
||||
mMockHalResultController = controller;
|
||||
mUserHasTrust = new SparseBooleanArray();
|
||||
mTrustManager = context.getSystemService(TrustManager.class);
|
||||
mTrustManager.registerTrustListener(this);
|
||||
mRandom = new Random();
|
||||
mFakeRejectRunnable = new FakeRejectRunnable();
|
||||
mFakeAcceptRunnable = new FakeAcceptRunnable();
|
||||
mRestartAuthRunnable = new RestartAuthRunnable(this, mScheduler);
|
||||
|
||||
// We can't initialize this during MockHalresultController's constructor due to a circular
|
||||
// dependency.
|
||||
mMockHalResultController.init(mRestartAuthRunnable, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrustChanged(boolean enabled, int userId, int flags) {
|
||||
mUserHasTrust.put(userId, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrustManagedChanged(boolean enabled, int userId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrustError(CharSequence message) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
FingerprintSensorProperties getFingerprintSensorProperties() {
|
||||
return mSensorProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onFingerDown(int x, int y, float minor, float major) {
|
||||
mHandler.post(() -> {
|
||||
Slog.d(TAG, "onFingerDown");
|
||||
final AuthenticationConsumer lastAuthenticatedConsumer =
|
||||
mMockHalResultController.getLastAuthenticatedClient();
|
||||
final ClientMonitor<?> currentScheduledClient = mScheduler.getCurrentClient();
|
||||
|
||||
if (currentScheduledClient == null) {
|
||||
Slog.d(TAG, "Not authenticating");
|
||||
return;
|
||||
}
|
||||
|
||||
mHandler.removeCallbacks(mFakeRejectRunnable);
|
||||
mHandler.removeCallbacks(mFakeAcceptRunnable);
|
||||
|
||||
// The sensor was authenticated, is still the currently scheduled client, and the
|
||||
// user touched the UDFPS affordance. Pretend that auth succeeded.
|
||||
final boolean authenticatedClientIsCurrent = lastAuthenticatedConsumer != null
|
||||
&& lastAuthenticatedConsumer == currentScheduledClient;
|
||||
// User is unlocked on keyguard via Trust Agent
|
||||
final boolean keyguardAndTrusted;
|
||||
if (currentScheduledClient instanceof FingerprintAuthenticationClient) {
|
||||
keyguardAndTrusted = ((FingerprintAuthenticationClient) currentScheduledClient)
|
||||
.isKeyguard()
|
||||
&& mUserHasTrust.get(currentScheduledClient.getTargetUserId(), false);
|
||||
} else {
|
||||
keyguardAndTrusted = false;
|
||||
}
|
||||
|
||||
final int captureDuration = getNewCaptureDuration();
|
||||
final int matchingDuration = getMatchingDuration();
|
||||
final int totalDuration = captureDuration + matchingDuration;
|
||||
setDebugMessage("Duration: " + totalDuration
|
||||
+ " (" + captureDuration + " + " + matchingDuration + ")");
|
||||
if (authenticatedClientIsCurrent || keyguardAndTrusted) {
|
||||
mFakeAcceptRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
|
||||
mHandler.postDelayed(mFakeAcceptRunnable, totalDuration);
|
||||
} else if (currentScheduledClient instanceof AuthenticationConsumer) {
|
||||
// Something is authenticating but authentication has not succeeded yet. Pretend
|
||||
// that auth rejected.
|
||||
mFakeRejectRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
|
||||
mHandler.postDelayed(mFakeRejectRunnable, totalDuration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
void onFingerUp() {
|
||||
mHandler.post(() -> {
|
||||
Slog.d(TAG, "onFingerUp");
|
||||
|
||||
// Only one of these can be on the handler at any given time (see onFingerDown). If
|
||||
// image capture is not complete, send ACQUIRED_TOO_FAST and remove the runnable from
|
||||
// the handler. Image capture (onFingerDown) needs to happen again.
|
||||
if (mHandler.hasCallbacks(mFakeRejectRunnable)
|
||||
&& !mFakeRejectRunnable.isImageCaptureComplete()) {
|
||||
mHandler.removeCallbacks(mFakeRejectRunnable);
|
||||
mMockHalResultController.onAcquired(0 /* deviceId */,
|
||||
FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
|
||||
0 /* vendorCode */);
|
||||
} else if (mHandler.hasCallbacks(mFakeAcceptRunnable)
|
||||
&& !mFakeAcceptRunnable.isImageCaptureComplete()) {
|
||||
mHandler.removeCallbacks(mFakeAcceptRunnable);
|
||||
mMockHalResultController.onAcquired(0 /* deviceId */,
|
||||
FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
|
||||
0 /* vendorCode */);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int getNewCaptureDuration() {
|
||||
final ContentResolver contentResolver = mContext.getContentResolver();
|
||||
final int captureTime = Settings.Secure.getIntForUser(contentResolver,
|
||||
CONFIG_AUTH_DELAY_PT1,
|
||||
DEFAULT_AUTH_DELAY_PT1_MS,
|
||||
UserHandle.USER_CURRENT);
|
||||
final int randomDelayRange = Settings.Secure.getIntForUser(contentResolver,
|
||||
CONFIG_AUTH_DELAY_RANDOMNESS,
|
||||
DEFAULT_AUTH_DELAY_RANDOMNESS_MS,
|
||||
UserHandle.USER_CURRENT);
|
||||
final int randomDelay = mRandom.nextInt(randomDelayRange * 2) - randomDelayRange;
|
||||
|
||||
// Must be at least 0
|
||||
return Math.max(captureTime + randomDelay, 0);
|
||||
}
|
||||
|
||||
private int getMatchingDuration() {
|
||||
final int matchingTime = Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
CONFIG_AUTH_DELAY_PT2,
|
||||
DEFAULT_AUTH_DELAY_PT2_MS,
|
||||
UserHandle.USER_CURRENT);
|
||||
|
||||
// Must be at least 0
|
||||
return Math.max(matchingTime, 0);
|
||||
}
|
||||
|
||||
private void setDebugMessage(String message) {
|
||||
try {
|
||||
final IUdfpsOverlayController controller = getUdfpsOverlayController();
|
||||
// Things can happen before SysUI loads and sets the controller.
|
||||
if (controller != null) {
|
||||
Slog.d(TAG, "setDebugMessage: " + message);
|
||||
controller.setDebugMessage(message);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception when sending message: " + message, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.server.biometrics.sensors.AuthenticationClient;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
|
||||
@@ -47,6 +46,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
|
||||
|
||||
private static final String TAG = "Biometrics/FingerprintAuthClient";
|
||||
|
||||
private final boolean mIsKeyguard;
|
||||
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
|
||||
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
|
||||
|
||||
@@ -54,16 +54,17 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
|
||||
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
|
||||
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
|
||||
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
|
||||
int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient,
|
||||
int sensorId, boolean isStrongBiometric, int statsClient,
|
||||
@NonNull TaskStackListener taskStackListener,
|
||||
@NonNull LockoutFrameworkImpl lockoutTracker,
|
||||
@Nullable IUdfpsOverlayController udfpsOverlayController) {
|
||||
@Nullable IUdfpsOverlayController udfpsOverlayController, boolean isKeyguard) {
|
||||
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
|
||||
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
|
||||
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
|
||||
lockoutTracker);
|
||||
mLockoutFrameworkImpl = lockoutTracker;
|
||||
mUdfpsOverlayController = udfpsOverlayController;
|
||||
mIsKeyguard = isKeyguard;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,4 +146,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
|
||||
public void onFingerUp() {
|
||||
UdfpsHelper.onFingerUp(getFreshDaemon());
|
||||
}
|
||||
|
||||
public boolean isKeyguard() {
|
||||
return mIsKeyguard;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,14 +38,17 @@ import android.hardware.fingerprint.IFingerprintService;
|
||||
import android.hardware.fingerprint.IFingerprintServiceReceiver;
|
||||
import android.hardware.fingerprint.IUdfpsOverlayController;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.NativeHandle;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.EventLog;
|
||||
import android.util.Slog;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.SystemService;
|
||||
@@ -158,8 +161,8 @@ public class FingerprintService extends SystemService {
|
||||
final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD
|
||||
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
|
||||
mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
|
||||
new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
|
||||
restricted, statsClient);
|
||||
new ClientMonitorCallbackConverter(receiver), opPackageName,
|
||||
restricted, statsClient, isKeyguard);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,8 +196,8 @@ public class FingerprintService extends SystemService {
|
||||
|
||||
final boolean restricted = true; // BiometricPrompt is always restricted
|
||||
mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie,
|
||||
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, surface,
|
||||
restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT);
|
||||
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
|
||||
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -369,8 +372,18 @@ public class FingerprintService extends SystemService {
|
||||
@Override // Binder call
|
||||
public void initializeConfiguration(int sensorId) {
|
||||
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
|
||||
mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetDispatcher,
|
||||
mGestureAvailabilityDispatcher);
|
||||
|
||||
if ((Build.IS_USERDEBUG || Build.IS_ENG)
|
||||
&& getContext().getResources().getBoolean(R.bool.allow_test_udfps)
|
||||
&& Settings.Secure.getIntForUser(getContext().getContentResolver(),
|
||||
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
|
||||
UserHandle.USER_CURRENT) != 0) {
|
||||
mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
|
||||
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
|
||||
} else {
|
||||
mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId,
|
||||
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1156,7 +1156,7 @@ public class TrustManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private void enforceListenerPermission() {
|
||||
mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
|
||||
mContext.enforceCallingOrSelfPermission(Manifest.permission.TRUST_LISTENER,
|
||||
"register trust listener");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user