Merge "Enable RRS (FHD<->QHD) support for UDFPS" into tm-dev

This commit is contained in:
Ilya Matyukhin
2022-04-21 05:55:03 +00:00
committed by Android (Google) Code Review
18 changed files with 599 additions and 269 deletions

View File

@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@@ -109,4 +110,12 @@ public class SensorLocationInternal implements Parcelable {
+ ", y: " + sensorLocationY + ", y: " + sensorLocationY
+ ", r: " + sensorRadius + "]"; + ", r: " + sensorRadius + "]";
} }
/** Returns coordinates of a bounding box around the sensor. */
public Rect getRect() {
return new Rect(sensorLocationX - sensorRadius,
sensorLocationY - sensorRadius,
sensorLocationX + sensorRadius,
sensorLocationY + sensorRadius);
}
} }

View File

@@ -87,6 +87,40 @@ public class RotationUtils {
rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation)); rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation));
} }
/**
* Rotates inOutBounds together with the parent for a given rotation delta. This assumes that
* the parent starts at 0,0 and remains at 0,0 after the rotation. The inOutBounds will remain
* at the same physical position within the parent.
*
* Only 'inOutBounds' is mutated.
*/
public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
@Rotation int rotation) {
final int origLeft = inOutBounds.left;
final int origTop = inOutBounds.top;
switch (rotation) {
case ROTATION_0:
return;
case ROTATION_90:
inOutBounds.left = inOutBounds.top;
inOutBounds.top = parentWidth - inOutBounds.right;
inOutBounds.right = inOutBounds.bottom;
inOutBounds.bottom = parentWidth - origLeft;
return;
case ROTATION_180:
inOutBounds.left = parentWidth - inOutBounds.right;
inOutBounds.right = parentWidth - origLeft;
inOutBounds.top = parentHeight - inOutBounds.bottom;
inOutBounds.bottom = parentHeight - origTop;
return;
case ROTATION_270:
inOutBounds.left = parentHeight - inOutBounds.bottom;
inOutBounds.bottom = inOutBounds.right;
inOutBounds.right = parentHeight - inOutBounds.top;
inOutBounds.top = origLeft;
}
}
/** /**
* Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
* 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
@@ -96,29 +130,7 @@ public class RotationUtils {
* Only 'inOutBounds' is mutated. * Only 'inOutBounds' is mutated.
*/ */
public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) { public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
final int origLeft = inOutBounds.left; rotateBounds(inOutBounds, parentBounds.right, parentBounds.bottom, rotation);
final int origTop = inOutBounds.top;
switch (rotation) {
case ROTATION_0:
return;
case ROTATION_90:
inOutBounds.left = inOutBounds.top;
inOutBounds.top = parentBounds.right - inOutBounds.right;
inOutBounds.right = inOutBounds.bottom;
inOutBounds.bottom = parentBounds.right - origLeft;
return;
case ROTATION_180:
inOutBounds.left = parentBounds.right - inOutBounds.right;
inOutBounds.right = parentBounds.right - origLeft;
inOutBounds.top = parentBounds.bottom - inOutBounds.bottom;
inOutBounds.bottom = parentBounds.bottom - origTop;
return;
case ROTATION_270:
inOutBounds.left = parentBounds.bottom - inOutBounds.bottom;
inOutBounds.bottom = inOutBounds.right;
inOutBounds.right = parentBounds.bottom - inOutBounds.top;
inOutBounds.top = origLeft;
}
} }
/** @return the rotation needed to rotate from oldRotation to newRotation. */ /** @return the rotation needed to rotate from oldRotation to newRotation. */

View File

@@ -30,7 +30,9 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.Rect;
import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator.Modality; import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricConstants;
@@ -54,6 +56,7 @@ import android.os.RemoteException;
import android.os.UserManager; import android.os.UserManager;
import android.util.Log; import android.util.Log;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.view.DisplayInfo;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.WindowManager; import android.view.WindowManager;
@@ -104,16 +107,16 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private final CommandQueue mCommandQueue; private final CommandQueue mCommandQueue;
private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController;
private final ActivityTaskManager mActivityTaskManager; private final ActivityTaskManager mActivityTaskManager;
@Nullable @Nullable private final FingerprintManager mFingerprintManager;
private final FingerprintManager mFingerprintManager; @Nullable private final FaceManager mFaceManager;
@Nullable
private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory; private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory; private final Provider<SidefpsController> mSidefpsControllerFactory;
@Nullable
private final PointF mFaceAuthSensorLocation; @NonNull private Point mStableDisplaySize = new Point();
@Nullable
private PointF mFingerprintLocation; @Nullable private final PointF mFaceAuthSensorLocation;
@Nullable private PointF mFingerprintLocation;
@Nullable private Rect mUdfpsBounds;
private final Set<Callback> mCallbacks = new HashSet<>(); private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState // TODO: These should just be saved from onSaveState
@@ -122,14 +125,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
AuthDialog mCurrentDialog; AuthDialog mCurrentDialog;
@NonNull private final WindowManager mWindowManager; @NonNull private final WindowManager mWindowManager;
@NonNull private final DisplayManager mDisplayManager;
@Nullable private UdfpsController mUdfpsController; @Nullable private UdfpsController mUdfpsController;
@Nullable private IUdfpsHbmListener mUdfpsHbmListener; @Nullable private IUdfpsHbmListener mUdfpsHbmListener;
@Nullable private SidefpsController mSidefpsController; @Nullable private SidefpsController mSidefpsController;
@Nullable private IBiometricContextListener mBiometricContextListener; @Nullable private IBiometricContextListener mBiometricContextListener;
@VisibleForTesting @VisibleForTesting IBiometricSysuiReceiver mReceiver;
IBiometricSysuiReceiver mReceiver; @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@VisibleForTesting
@NonNull final BiometricDisplayListener mOrientationListener;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps; @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mFpProps; @Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps; @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -249,6 +251,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
} }
mAllFingerprintAuthenticatorsRegistered = true; mAllFingerprintAuthenticatorsRegistered = true;
mFpProps = sensors; mFpProps = sensors;
List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>(); List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>(); List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
for (FingerprintSensorPropertiesInternal props : mFpProps) { for (FingerprintSensorPropertiesInternal props : mFpProps) {
@@ -259,12 +262,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
sidefpsProps.add(props); sidefpsProps.add(props);
} }
} }
mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null; mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
if (mUdfpsProps != null) { if (mUdfpsProps != null) {
mUdfpsController = mUdfpsControllerFactory.get(); mUdfpsController = mUdfpsControllerFactory.get();
mUdfpsController.addCallback(new UdfpsController.Callback() { mUdfpsController.addCallback(new UdfpsController.Callback() {
@Override @Override
public void onFingerUp() {} public void onFingerUp() {
}
@Override @Override
public void onFingerDown() { public void onFingerDown() {
@@ -273,15 +278,22 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
} }
} }
}); });
mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
updateUdfpsLocation();
} }
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null; mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) { if (mSidefpsProps != null) {
mSidefpsController = mSidefpsControllerFactory.get(); mSidefpsController = mSidefpsControllerFactory.get();
} }
mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
updateFingerprintLocation();
for (Callback cb : mCallbacks) { for (Callback cb : mCallbacks) {
cb.onAllAuthenticatorsRegistered(); cb.onAllAuthenticatorsRegistered();
} }
mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
} }
private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
@@ -424,12 +436,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
/** /**
* @return where the UDFPS exists on the screen in pixels in portrait mode. * @return where the UDFPS exists on the screen in pixels in portrait mode.
*/ */
@Nullable public PointF getUdfpsSensorLocation() { @Nullable public PointF getUdfpsLocation() {
if (mUdfpsController == null) { if (mUdfpsController == null || mUdfpsBounds == null) {
return null; return null;
} }
return new PointF(mUdfpsController.getSensorLocation().centerX(), return new PointF(mUdfpsBounds.centerX(), mUdfpsBounds.centerY());
mUdfpsController.getSensorLocation().centerY());
} }
/** /**
@@ -437,8 +448,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
* overridden value will use the default value even if they don't have a fingerprint sensor * overridden value will use the default value even if they don't have a fingerprint sensor
*/ */
@Nullable public PointF getFingerprintSensorLocation() { @Nullable public PointF getFingerprintSensorLocation() {
if (getUdfpsSensorLocation() != null) { if (getUdfpsLocation() != null) {
return getUdfpsSensorLocation(); return getUdfpsLocation();
} }
return mFingerprintLocation; return mFingerprintLocation;
} }
@@ -525,12 +536,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mFaceManager = faceManager; mFaceManager = faceManager;
mUdfpsControllerFactory = udfpsControllerFactory; mUdfpsControllerFactory = udfpsControllerFactory;
mSidefpsControllerFactory = sidefpsControllerFactory; mSidefpsControllerFactory = sidefpsControllerFactory;
mDisplayManager = displayManager;
mWindowManager = windowManager; mWindowManager = windowManager;
mUdfpsEnrolledForUser = new SparseBooleanArray(); mUdfpsEnrolledForUser = new SparseBooleanArray();
mOrientationListener = new BiometricDisplayListener( mOrientationListener = new BiometricDisplayListener(
context, context,
displayManager, mDisplayManager,
mHandler, mHandler,
BiometricDisplayListener.SensorType.Generic.INSTANCE, BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> { () -> {
@@ -582,6 +594,27 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
yLocation); yLocation);
} }
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
// This is not combined with updateFingerprintLocation because this is invoked directly from
// UdfpsController, only when it cares about a rotation change. The implications of calling
// updateFingerprintLocation in such a case are unclear.
private void updateUdfpsLocation() {
if (mUdfpsController != null) {
final DisplayInfo displayInfo = new DisplayInfo();
mContext.getDisplay().getDisplayInfo(displayInfo);
final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
mStableDisplaySize.x, mStableDisplaySize.y, displayInfo.getNaturalWidth(),
displayInfo.getNaturalHeight());
final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
mUdfpsBounds = udfpsProp.getLocation().getRect();
mUdfpsBounds.scale(scaleFactor);
mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(),
displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation));
}
}
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void start() { public void start() {
@@ -592,6 +625,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mFingerprintAuthenticatorsRegisteredCallback); mFingerprintAuthenticatorsRegisteredCallback);
} }
mStableDisplaySize = mDisplayManager.getStableDisplaySize();
mActivityTaskManager.registerTaskStackListener(mTaskStackListener); mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
} }
@@ -906,6 +940,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
protected void onConfigurationChanged(Configuration newConfig) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
updateFingerprintLocation(); updateFingerprintLocation();
updateUdfpsLocation();
// Save the state of the current dialog (buttons showing, etc) // Save the state of the current dialog (buttons showing, etc)
if (mCurrentDialog != null) { if (mCurrentDialog != null) {
@@ -935,6 +970,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private void onOrientationChanged() { private void onOrientationChanged() {
updateFingerprintLocation(); updateFingerprintLocation();
updateUdfpsLocation();
if (mCurrentDialog != null) { if (mCurrentDialog != null) {
mCurrentDialog.onOrientationChanged(); mCurrentDialog.onOrientationChanged();
} }

View File

@@ -64,7 +64,11 @@ class BiometricDisplayListener(
/** Listen for changes. */ /** Listen for changes. */
fun enable() { fun enable() {
lastRotation = context.display?.rotation ?: Surface.ROTATION_0 lastRotation = context.display?.rotation ?: Surface.ROTATION_0
displayManager.registerDisplayListener(this, handler) displayManager.registerDisplayListener(
this,
handler,
DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
)
} }
/** Stop listening for changes. */ /** Stop listening for changes. */
@@ -80,9 +84,7 @@ class BiometricDisplayListener(
*/ */
sealed class SensorType { sealed class SensorType {
object Generic : SensorType() object Generic : SensorType()
data class UnderDisplayFingerprint( object UnderDisplayFingerprint : SensorType()
val properties: FingerprintSensorPropertiesInternal
) : SensorType()
data class SideFingerprint( data class SideFingerprint(
val properties: FingerprintSensorPropertiesInternal val properties: FingerprintSensorPropertiesInternal
) : SensorType() ) : SensorType()

View File

@@ -19,7 +19,6 @@ package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD; import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
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;
@@ -30,12 +29,9 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.RectF;
import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.Handler; import android.os.Handler;
@@ -45,6 +41,7 @@ import android.os.Trace;
import android.os.VibrationAttributes; import android.os.VibrationAttributes;
import android.os.VibrationEffect; import android.os.VibrationEffect;
import android.util.Log; import android.util.Log;
import android.util.RotationUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface; import android.view.Surface;
@@ -99,7 +96,6 @@ import kotlin.Unit;
public class UdfpsController implements DozeReceiver { public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController"; private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds
// Minimum required delay between consecutive touch logs in milliseconds. // Minimum required delay between consecutive touch logs in milliseconds.
private static final long MIN_TOUCH_LOG_INTERVAL = 50; private static final long MIN_TOUCH_LOG_INTERVAL = 50;
@@ -129,10 +125,14 @@ public class UdfpsController implements DozeReceiver {
mUnlockedScreenOffAnimationController; mUnlockedScreenOffAnimationController;
@NonNull private final LatencyTracker mLatencyTracker; @NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener; @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated. // sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @VisibleForTesting int mSensorId;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; @VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider; @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
// Tracks the velocity of a touch to help filter out the touches that move too fast. // Tracks the velocity of a touch to help filter out the touches that move too fast.
@@ -193,19 +193,16 @@ public class UdfpsController implements DozeReceiver {
@Override @Override
public void showUdfpsOverlay(long requestId, int sensorId, int reason, public void showUdfpsOverlay(long requestId, int sensorId, int reason,
@NonNull IUdfpsOverlayControllerCallback callback) { @NonNull IUdfpsOverlayControllerCallback callback) {
mFgExecutor.execute( mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
() -> UdfpsController.this.showUdfpsOverlay(new UdfpsControllerOverlay( new UdfpsControllerOverlay(mContext, mFingerprintManager, mInflater,
mContext, mFingerprintManager, mInflater, mWindowManager, mWindowManager, mAccessibilityManager, mStatusBarStateController,
mAccessibilityManager, mStatusBarStateController,
mPanelExpansionStateManager, mKeyguardViewManager, mPanelExpansionStateManager, mKeyguardViewManager,
mKeyguardUpdateMonitor, mDialogManager, mDumpManager, mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
mLockscreenShadeTransitionController, mConfigurationController, mLockscreenShadeTransitionController, mConfigurationController,
mSystemClock, mKeyguardStateController, mSystemClock, mKeyguardStateController,
mUnlockedScreenOffAnimationController, mSensorProps, mUnlockedScreenOffAnimationController, mHbmProvider, requestId, reason,
mHbmProvider, requestId, reason, callback, callback, (view, event, fromUdfpsView) -> onTouch(requestId, event,
(view, event, fromUdfpsView) -> fromUdfpsView), mActivityLaunchAnimator)));
onTouch(requestId, event, fromUdfpsView),
mActivityLaunchAnimator)));
} }
@Override @Override
@@ -280,6 +277,38 @@ public class UdfpsController implements DozeReceiver {
} }
} }
/**
* Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
*
* @param sensorId sensor for which the overlay is getting updated.
* @param overlayParams See {@link UdfpsOverlayParams}.
*/
public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
if (sensorId != mSensorId) {
mSensorId = sensorId;
Log.w(TAG, "updateUdfpsParams | sensorId has changed");
}
if (!mOverlayParams.equals(overlayParams)) {
mOverlayParams = overlayParams;
final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
// When the bounds change it's always necessary to re-create the overlay's window with
// new LayoutParams. If the overlay needs to be shown, this will re-create and show the
// overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
redrawOverlay();
if (wasShowingAltAuth) {
mKeyguardViewManager.showGenericBouncer(true);
}
}
}
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
public void setAuthControllerUpdateUdfpsLocation(@Nullable Runnable r) {
mAuthControllerUpdateUdfpsLocation = r;
}
/** /**
* Calculate the pointer speed given a velocity tracker and the pointer id. * Calculate the pointer speed given a velocity tracker and the pointer id.
* This assumes that the velocity tracker has already been passed all relevant motion events. * This assumes that the velocity tracker has already been passed all relevant motion events.
@@ -340,10 +369,11 @@ public class UdfpsController implements DozeReceiver {
} }
return !mOverlay.getAnimationViewController().shouldPauseAuth() return !mOverlay.getAnimationViewController().shouldPauseAuth()
&& getSensorLocation().contains(x, y); && mOverlayParams.getSensorBounds().contains((int) x, (int) y);
} }
private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { @VisibleForTesting
boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (mOverlay == null) { if (mOverlay == null) {
Log.w(TAG, "ignoring onTouch with null overlay"); Log.w(TAG, "ignoring onTouch with null overlay");
return false; return false;
@@ -438,33 +468,28 @@ public class UdfpsController implements DozeReceiver {
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime; final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
if (!isIlluminationRequested && !mAcquiredReceived if (!isIlluminationRequested && !mAcquiredReceived
&& !exceedsVelocityThreshold) { && !exceedsVelocityThreshold) {
final int rawX = (int) event.getRawX(); // Map the touch to portrait mode if the device is in landscape mode.
final int rawY = (int) event.getRawY(); Point portraitTouch = new Point(
// Default coordinates assume portrait mode. (int) event.getRawX(idx),
int x = rawX; (int) event.getRawY(idx)
int y = rawY; );
final int rot = mOverlayParams.getRotation();
// Gets the size based on the current rotation of the display. if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
Point p = new Point(); RotationUtils.rotatePoint(portraitTouch,
mContext.getDisplay().getRealSize(p); RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
mOverlayParams.getLogicalDisplayWidth(),
// Transform x, y to portrait mode if the device is in landscape mode. mOverlayParams.getLogicalDisplayHeight()
switch (mContext.getDisplay().getRotation()) { );
case Surface.ROTATION_90:
x = p.y - rawY;
y = rawX;
break;
case Surface.ROTATION_270:
x = rawY;
y = p.x - rawX;
break;
default:
// Do nothing to stay in portrait mode.
} }
onFingerDown(requestId, x, y, minor, major); // Scale the coordinates to native resolution.
final float scale = mOverlayParams.getScaleFactor();
int scaledX = (int) (portraitTouch.x / scale);
int scaledY = (int) (portraitTouch.y / scale);
float scaledMinor = minor / scale;
float scaledMajor = major / scale;
onFingerDown(requestId, scaledX, scaledY, scaledMinor, scaledMajor);
Log.v(TAG, "onTouch | finger down: " + touchInfo); Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = mSystemClock.elapsedRealtime(); mTouchLogTime = mSystemClock.elapsedRealtime();
mPowerManager.userActivity(mSystemClock.uptimeMillis(), mPowerManager.userActivity(mSystemClock.uptimeMillis(),
@@ -571,16 +596,15 @@ public class UdfpsController implements DozeReceiver {
mActivityLaunchAnimator = activityLaunchAnimator; mActivityLaunchAnimator = activityLaunchAnimator;
mAlternateTouchProvider = aternateTouchProvider.orElse(null); mAlternateTouchProvider = aternateTouchProvider.orElse(null);
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
checkArgument(mSensorProps != null);
mOrientationListener = new BiometricDisplayListener( mOrientationListener = new BiometricDisplayListener(
context, context,
displayManager, displayManager,
mainHandler, mainHandler,
new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps), BiometricDisplayListener.SensorType.UnderDisplayFingerprint.INSTANCE,
() -> { () -> {
onOrientationChanged(); if (mAuthControllerUpdateUdfpsLocation != null) {
mAuthControllerUpdateUdfpsLocation.run();
}
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
@@ -609,17 +633,6 @@ public class UdfpsController implements DozeReceiver {
} }
} }
@Nullable
private FingerprintSensorPropertiesInternal findFirstUdfps() {
for (FingerprintSensorPropertiesInternal props :
mFingerprintManager.getSensorPropertiesInternal()) {
if (props.isAnyUdfpsType()) {
return props;
}
}
return null;
}
@Override @Override
public void dozeTimeTick() { public void dozeTimeTick() {
if (mOverlay != null) { if (mOverlay != null) {
@@ -630,21 +643,6 @@ public class UdfpsController implements DozeReceiver {
} }
} }
/**
* @return where the UDFPS exists on the screen in pixels.
*/
public RectF getSensorLocation() {
// This is currently used to calculate the amount of space available for notifications
// on lockscreen and for the udfps light reveal animation on keyguard.
// Keyguard is only shown in portrait mode for now, so this will need to
// be updated if that ever changes.
final SensorLocationInternal location = mSensorProps.getLocation();
return new RectF(location.sensorLocationX - location.sensorRadius,
location.sensorLocationY - location.sensorRadius,
location.sensorLocationX + location.sensorRadius,
location.sensorLocationY + location.sensorRadius);
}
private void redrawOverlay() { private void redrawOverlay() {
UdfpsControllerOverlay overlay = mOverlay; UdfpsControllerOverlay overlay = mOverlay;
if (overlay != null) { if (overlay != null) {
@@ -653,26 +651,11 @@ public class UdfpsController implements DozeReceiver {
} }
} }
private void onOrientationChanged() {
// When the configuration changes it's almost always necessary to destroy and re-create
// the overlay's window to pass it the new LayoutParams.
// Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
// of whether it is already hidden.
final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
// If the overlay needs to be shown, this will re-create and show the overlay with the
// updated LayoutParams. Otherwise, the overlay will remain hidden.
redrawOverlay();
if (wasShowingAltAuth) {
mKeyguardViewManager.showGenericBouncer(true);
}
}
private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) { private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) {
mExecution.assertIsMainThread(); mExecution.assertIsMainThread();
mOverlay = overlay; mOverlay = overlay;
if (overlay.show(this)) { if (overlay.show(this, mOverlayParams)) {
Log.v(TAG, "showUdfpsOverlay | adding window reason=" Log.v(TAG, "showUdfpsOverlay | adding window reason="
+ overlay.getRequestReason()); + overlay.getRequestReason());
mOnFingerDown = false; mOnFingerDown = false;
@@ -814,14 +797,14 @@ public class UdfpsController implements DozeReceiver {
if (mAlternateTouchProvider != null) { if (mAlternateTouchProvider != null) {
mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major); mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
} else { } else {
mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major); mFingerprintManager.onPointerDown(requestId, mSensorId, x, y, minor, major);
} }
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView(); final UdfpsView view = mOverlay.getOverlayView();
if (view != null) { if (view != null) {
view.startIllumination(() -> { view.startIllumination(() -> {
mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId); mFingerprintManager.onUiReady(requestId, mSensorId);
mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
}); });
} }
@@ -839,7 +822,7 @@ public class UdfpsController implements DozeReceiver {
if (mAlternateTouchProvider != null) { if (mAlternateTouchProvider != null) {
mAlternateTouchProvider.onPointerUp(requestId); mAlternateTouchProvider.onPointerUp(requestId);
} else { } else {
mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId); mFingerprintManager.onPointerUp(requestId, mSensorId);
} }
for (Callback cb : mCallbacks) { for (Callback cb : mCallbacks) {
cb.onFingerUp(); cb.onFingerUp();

View File

@@ -20,17 +20,16 @@ import android.annotation.SuppressLint
import android.annotation.UiThread import android.annotation.UiThread
import android.content.Context import android.content.Context
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.graphics.Point import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants import android.hardware.biometrics.BiometricOverlayConstants
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.os.RemoteException import android.os.RemoteException
import android.util.Log import android.util.Log
import android.util.RotationUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.Surface import android.view.Surface
@@ -78,7 +77,6 @@ class UdfpsControllerOverlay(
private val systemClock: SystemClock, private val systemClock: SystemClock,
private val keyguardStateController: KeyguardStateController, private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
private val sensorProps: FingerprintSensorPropertiesInternal,
private var hbmProvider: UdfpsHbmProvider, private var hbmProvider: UdfpsHbmProvider,
val requestId: Long, val requestId: Long,
@ShowReason val requestReason: Int, @ShowReason val requestReason: Int,
@@ -90,6 +88,8 @@ class UdfpsControllerOverlay(
var overlayView: UdfpsView? = null var overlayView: UdfpsView? = null
private set private set
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
private var overlayTouchListener: TouchExplorationStateChangeListener? = null private var overlayTouchListener: TouchExplorationStateChangeListener? = null
private val coreLayoutParams = WindowManager.LayoutParams( private val coreLayoutParams = WindowManager.LayoutParams(
@@ -101,7 +101,11 @@ class UdfpsControllerOverlay(
fitInsetsTypes = 0 fitInsetsTypes = 0
gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
flags =
(Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
// Avoid announcing window title.
accessibilityTitle = " "
} }
/** A helper if the [requestReason] was due to enrollment. */ /** A helper if the [requestReason] was due to enrollment. */
@@ -125,13 +129,14 @@ class UdfpsControllerOverlay(
/** Show the overlay or return false and do nothing if it is already showing. */ /** Show the overlay or return false and do nothing if it is already showing. */
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
fun show(controller: UdfpsController): Boolean { fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
if (overlayView == null) { if (overlayView == null) {
overlayParams = params
try { try {
overlayView = (inflater.inflate( overlayView = (inflater.inflate(
R.layout.udfps_view, null, false R.layout.udfps_view, null, false
) as UdfpsView).apply { ) as UdfpsView).apply {
sensorProperties = sensorProps overlayParams = params
setHbmProvider(hbmProvider) setHbmProvider(hbmProvider)
val animation = inflateUdfpsAnimation(this, controller) val animation = inflateUdfpsAnimation(this, controller)
if (animation != null) { if (animation != null) {
@@ -144,8 +149,7 @@ class UdfpsControllerOverlay(
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
} }
windowManager.addView(this, windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
coreLayoutParams.updateForLocation(sensorProps.location, animation))
overlayTouchListener = TouchExplorationStateChangeListener { overlayTouchListener = TouchExplorationStateChangeListener {
if (accessibilityManager.isTouchExplorationEnabled) { if (accessibilityManager.isTouchExplorationEnabled) {
@@ -180,13 +184,14 @@ class UdfpsControllerOverlay(
REASON_ENROLL_ENROLLING -> { REASON_ENROLL_ENROLLING -> {
UdfpsEnrollViewController( UdfpsEnrollViewController(
view.addUdfpsView(R.layout.udfps_enroll_view) { view.addUdfpsView(R.layout.udfps_enroll_view) {
updateSensorLocation(sensorProps) updateSensorLocation(overlayParams.sensorBounds)
}, },
enrollHelper ?: throw IllegalStateException("no enrollment helper"), enrollHelper ?: throw IllegalStateException("no enrollment helper"),
statusBarStateController, statusBarStateController,
panelExpansionStateManager, panelExpansionStateManager,
dialogManager, dialogManager,
dumpManager dumpManager,
overlayParams.scaleFactor
) )
} }
BiometricOverlayConstants.REASON_AUTH_KEYGUARD -> { BiometricOverlayConstants.REASON_AUTH_KEYGUARD -> {
@@ -280,57 +285,42 @@ class UdfpsControllerOverlay(
/** Checks if the id is relevant for this overlay. */ /** Checks if the id is relevant for this overlay. */
fun matchesRequestId(id: Long): Boolean = requestId == -1L || requestId == id fun matchesRequestId(id: Long): Boolean = requestId == -1L || requestId == id
private fun WindowManager.LayoutParams.updateForLocation( private fun WindowManager.LayoutParams.updateDimensions(
location: SensorLocationInternal,
animation: UdfpsAnimationViewController<*>? animation: UdfpsAnimationViewController<*>?
): WindowManager.LayoutParams { ): WindowManager.LayoutParams {
val paddingX = animation?.paddingX ?: 0 val paddingX = animation?.paddingX ?: 0
val paddingY = animation?.paddingY ?: 0 val paddingY = animation?.paddingY ?: 0
flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS
or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
if (animation != null && animation.listenForTouchesOutsideView()) { if (animation != null && animation.listenForTouchesOutsideView()) {
flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
} }
// Default dimensions assume portrait mode. // Original sensorBounds assume portrait mode.
x = location.sensorLocationX - location.sensorRadius - paddingX val rotatedSensorBounds = Rect(overlayParams.sensorBounds)
y = location.sensorLocationY - location.sensorRadius - paddingY
height = 2 * location.sensorRadius + 2 * paddingX
width = 2 * location.sensorRadius + 2 * paddingY
// Gets the size based on the current rotation of the display. val rot = overlayParams.rotation
val p = Point() if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
context.display!!.getRealSize(p) if (!shouldRotate(animation)) {
when (context.display!!.rotation) { Log.v(
Surface.ROTATION_90 -> { TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
if (!shouldRotate(animation)) {
Log.v(TAG, "skip rotating udfps location ROTATION_90" +
" animation=$animation" + " animation=$animation" +
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" + " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
" isOccluded=${keyguardStateController.isOccluded}") " isOccluded=${keyguardStateController.isOccluded}"
} else { )
Log.v(TAG, "rotate udfps location ROTATION_90") } else {
x = (location.sensorLocationY - location.sensorRadius - paddingX) Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
y = (p.y - location.sensorLocationX - location.sensorRadius - paddingY) RotationUtils.rotateBounds(
} rotatedSensorBounds,
overlayParams.naturalDisplayWidth,
overlayParams.naturalDisplayHeight,
rot
)
} }
Surface.ROTATION_270 -> {
if (!shouldRotate(animation)) {
Log.v(TAG, "skip rotating udfps location ROTATION_270" +
" animation=$animation" +
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
" isOccluded=${keyguardStateController.isOccluded}")
} else {
Log.v(TAG, "rotate udfps location ROTATION_270")
x = (p.x - location.sensorLocationY - location.sensorRadius - paddingX)
y = (location.sensorLocationX - location.sensorRadius - paddingY)
}
}
else -> {}
} }
// avoid announcing window title x = rotatedSensorBounds.left - paddingX
accessibilityTitle = " " y = rotatedSensorBounds.top - paddingY
height = rotatedSensorBounds.height() + 2 * paddingX
width = rotatedSensorBounds.width() + 2 * paddingY
return this return this
} }
@@ -363,5 +353,5 @@ private fun Int.isEnrollmentReason() =
@ShowReason @ShowReason
private fun Int.isImportantForAccessibility() = private fun Int.isImportantForAccessibility() =
this == REASON_ENROLL_FIND_SENSOR || this == REASON_ENROLL_FIND_SENSOR ||
this == REASON_ENROLL_ENROLLING || this == REASON_ENROLL_ENROLLING ||
this == BiometricOverlayConstants.REASON_AUTH_BP this == BiometricOverlayConstants.REASON_AUTH_BP

View File

@@ -17,7 +17,7 @@
package com.android.systemui.biometrics; package com.android.systemui.biometrics;
import android.content.Context; import android.content.Context;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.graphics.Rect;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -61,13 +61,11 @@ public class UdfpsEnrollView extends UdfpsAnimationView {
return mFingerprintDrawable; return mFingerprintDrawable;
} }
void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) { void updateSensorLocation(@NonNull Rect sensorBounds) {
View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view); View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
final int sensorHeight = sensorProps.getLocation().sensorRadius * 2;
final int sensorWidth = sensorHeight;
ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams(); ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
params.width = sensorWidth; params.width = sensorBounds.width();
params.height = sensorHeight; params.height = sensorBounds.height();
fingerprintAccessibilityView.setLayoutParams(params); fingerprintAccessibilityView.setLayoutParams(params);
fingerprintAccessibilityView.requestLayout(); fingerprintAccessibilityView.requestLayout();
} }

View File

@@ -56,11 +56,12 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
@NonNull StatusBarStateController statusBarStateController, @NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager, @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull SystemUIDialogManager systemUIDialogManager, @NonNull SystemUIDialogManager systemUIDialogManager,
@NonNull DumpManager dumpManager) { @NonNull DumpManager dumpManager,
float scaleFactor) {
super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager, super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
dumpManager); dumpManager);
mEnrollProgressBarRadius = getContext().getResources() mEnrollProgressBarRadius = (int) (scaleFactor * getContext().getResources().getInteger(
.getInteger(R.integer.config_udfpsEnrollProgressBar); R.integer.config_udfpsEnrollProgressBar));
mEnrollHelper = enrollHelper; mEnrollHelper = enrollHelper;
mView.setEnrollHelper(mEnrollHelper); mView.setEnrollHelper(mEnrollHelper);
} }

View File

@@ -0,0 +1,43 @@
package com.android.systemui.biometrics
import android.graphics.Rect
import android.view.Surface
import android.view.Surface.Rotation
/**
* Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
*
* @property sensorBounds coordinates of the bounding box around the sensor, in natural orientation,
* in pixels, for the current resolution.
* @property naturalDisplayWidth width of the physical display, in natural orientation, in pixels,
* for the current resolution.
* @property naturalDisplayHeight height of the physical display, in natural orientation, in pixels,
* for the current resolution.
* @property scaleFactor ratio of a dimension in the current resolution to the corresponding
* dimension in the native resolution.
* @property rotation current rotation of the display.
*/
data class UdfpsOverlayParams(
val sensorBounds: Rect = Rect(),
val naturalDisplayWidth: Int = 0,
val naturalDisplayHeight: Int = 0,
val scaleFactor: Float = 1f,
@Rotation val rotation: Int = Surface.ROTATION_0
) {
/** See [android.view.DisplayInfo.logicalWidth] */
val logicalDisplayWidth
get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
naturalDisplayHeight
} else {
naturalDisplayWidth
}
/** See [android.view.DisplayInfo.logicalHeight] */
val logicalDisplayHeight
get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
naturalDisplayWidth
} else {
naturalDisplayHeight
}
}

View File

@@ -21,7 +21,6 @@ import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.PointF import android.graphics.PointF
import android.graphics.RectF import android.graphics.RectF
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log import android.util.Log
import android.view.MotionEvent import android.view.MotionEvent
@@ -39,6 +38,8 @@ class UdfpsView(
attrs: AttributeSet? attrs: AttributeSet?
) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator { ) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
// sensorRect may be bigger than the sensor. True sensor dimensions are defined in
// overlayParams.sensorBounds
private val sensorRect = RectF() private val sensorRect = RectF()
private var hbmProvider: UdfpsHbmProvider? = null private var hbmProvider: UdfpsHbmProvider? = null
private val debugTextPaint = Paint().apply { private val debugTextPaint = Paint().apply {
@@ -62,8 +63,8 @@ class UdfpsView(
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */ /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
var animationViewController: UdfpsAnimationViewController<*>? = null var animationViewController: UdfpsAnimationViewController<*>? = null
/** Properties used to obtain the sensor location. */ /** Parameters that affect the position and size of the overlay. Visible for testing. */
var sensorProperties: FingerprintSensorPropertiesInternal? = null var overlayParams = UdfpsOverlayParams()
/** Debug message. */ /** Debug message. */
var debugMessage: String? = null var debugMessage: String? = null
@@ -94,13 +95,12 @@ class UdfpsView(
val paddingX = animationViewController?.paddingX ?: 0 val paddingX = animationViewController?.paddingX ?: 0
val paddingY = animationViewController?.paddingY ?: 0 val paddingY = animationViewController?.paddingY ?: 0
val sensorRadius = sensorProperties?.location?.sensorRadius ?: 0
sensorRect.set( sensorRect.set(
paddingX.toFloat(), paddingX.toFloat(),
paddingY.toFloat(), paddingY.toFloat(),
(2 * sensorRadius + paddingX).toFloat(), (overlayParams.sensorBounds.width() + paddingX).toFloat(),
(2 * sensorRadius + paddingY).toFloat() (overlayParams.sensorBounds.height() + paddingY).toFloat()
) )
animationViewController?.onSensorRectUpdated(RectF(sensorRect)) animationViewController?.onSensorRectUpdated(RectF(sensorRect))
} }

View File

@@ -27,6 +27,7 @@ import static junit.framework.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
@@ -46,6 +47,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Point;
import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricManager;
@@ -185,6 +187,8 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDialog1.getRequestId()).thenReturn(REQUEST_ID); when(mDialog1.getRequestId()).thenReturn(REQUEST_ID);
when(mDialog2.getRequestId()).thenReturn(REQUEST_ID); when(mDialog2.getRequestId()).thenReturn(REQUEST_ID);
when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point());
when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -663,7 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSubscribesToOrientationChangesWhenShowingDialog() { public void testSubscribesToOrientationChangesWhenShowingDialog() {
showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler)); verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
mAuthController.hideAuthenticationDialog(REQUEST_ID); mAuthController.hideAuthenticationDialog(REQUEST_ID);
verify(mDisplayManager).unregisterDisplayListener(any()); verify(mDisplayManager).unregisterDisplayListener(any());

View File

@@ -163,7 +163,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
fun testFingerprintTrigger_KeyguardNotVisible_NotDreaming_NoRipple() { fun testFingerprintTrigger_KeyguardNotVisible_NotDreaming_NoRipple() {
// GIVEN fp exists & user doesn't need strong auth // GIVEN fp exists & user doesn't need strong auth
val fpsLocation = PointF(5f, 5f) val fpsLocation = PointF(5f, 5f)
`when`(authController.udfpsSensorLocation).thenReturn(fpsLocation) `when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached() controller.onViewAttached()
`when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false) `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
@@ -185,7 +185,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
fun testFingerprintTrigger_StrongAuthRequired_NoRipple() { fun testFingerprintTrigger_StrongAuthRequired_NoRipple() {
// GIVEN fp exists & keyguard is visible // GIVEN fp exists & keyguard is visible
val fpsLocation = PointF(5f, 5f) val fpsLocation = PointF(5f, 5f)
`when`(authController.udfpsSensorLocation).thenReturn(fpsLocation) `when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached() controller.onViewAttached()
`when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true) `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)

View File

@@ -20,6 +20,7 @@ import static com.android.systemui.biometrics.BiometricDisplayListener.SensorTyp
import static com.android.systemui.biometrics.BiometricDisplayListener.SensorType.UnderDisplayFingerprint; import static com.android.systemui.biometrics.BiometricDisplayListener.SensorType.UnderDisplayFingerprint;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same; import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
@@ -89,7 +90,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
mContextSpy, mDisplayManager, mHandler, mUdfpsType, mOnChangedCallback); mContextSpy, mDisplayManager, mHandler, mUdfpsType, mOnChangedCallback);
listener.enable(); listener.enable();
verify(mDisplayManager).registerDisplayListener(any(), same(mHandler)); verify(mDisplayManager).registerDisplayListener(any(), same(mHandler),
eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
} }
@Test @Test
@@ -113,7 +115,7 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_90); when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_90);
listener.enable(); listener.enable();
verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(), verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
same(mHandler)); same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
// Rotate the device back to portrait and ensure the rotation is detected. // Rotate the device back to portrait and ensure the rotation is detected.
when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_0); when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
@@ -150,8 +152,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
// The listener should record the current rotation and register a display listener. // The listener should record the current rotation and register a display listener.
verify(mDisplay).getRotation(); verify(mDisplay).getRotation();
verify(mDisplayManager) verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
.registerDisplayListener(mDisplayListenerCaptor.capture(), same(mHandler)); same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
// Test the first rotation since the listener was enabled. // Test the first rotation since the listener was enabled.
mDisplayListenerCaptor.getValue().onDisplayChanged(123); mDisplayListenerCaptor.getValue().onDisplayChanged(123);
@@ -182,8 +184,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
listener.enable(); listener.enable();
// The listener should register a display listener. // The listener should register a display listener.
verify(mDisplayManager) verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
.registerDisplayListener(mDisplayListenerCaptor.capture(), same(mHandler)); same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
// mOnChangedCallback should be invoked for all calls to onDisplayChanged. // mOnChangedCallback should be invoked for all calls to onDisplayChanged.
mDisplayListenerCaptor.getValue().onDisplayChanged(123); mDisplayListenerCaptor.getValue().onDisplayChanged(123);

View File

@@ -188,7 +188,7 @@ class SidefpsControllerTest : SysuiTestCase() {
overlayController.show(SENSOR_ID, REASON_UNKNOWN) overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady() executor.runAllReady()
verify(displayManager).registerDisplayListener(any(), eq(handler)) verify(displayManager).registerDisplayListener(any(), eq(handler), anyLong())
overlayController.hide(SENSOR_ID) overlayController.hide(SENSOR_ID)
executor.runAllReady() executor.runAllReady()

View File

@@ -16,6 +16,7 @@
package com.android.systemui.biometrics package com.android.systemui.biometrics
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
@@ -23,7 +24,6 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
@@ -31,6 +31,8 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.Surface
import android.view.Surface.Rotation
import android.view.WindowManager import android.view.WindowManager
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
@@ -53,8 +55,10 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
@@ -63,13 +67,18 @@ import org.mockito.Mockito.`when` as whenever
private const val REQUEST_ID = 2L private const val REQUEST_ID = 2L
// Dimensions for the current display resolution.
private const val DISPLAY_WIDTH = 1080
private const val DISPLAY_HEIGHT = 1920
private const val SENSOR_WIDTH = 30
private const val SENSOR_HEIGHT = 60
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true) @RunWithLooper(setAsMainLooper = true)
class UdfpsControllerOverlayTest : SysuiTestCase() { class UdfpsControllerOverlayTest : SysuiTestCase() {
@JvmField @Rule @JvmField @Rule var rule = MockitoJUnit.rule()
var rule = MockitoJUnit.rule()
@Mock private lateinit var fingerprintManager: FingerprintManager @Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var inflater: LayoutInflater @Mock private lateinit var inflater: LayoutInflater
@@ -85,18 +94,17 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var systemClock: SystemClock @Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock @Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
@Mock private lateinit var hbmProvider: UdfpsHbmProvider @Mock private lateinit var hbmProvider: UdfpsHbmProvider
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController @Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView @Mock private lateinit var udfpsView: UdfpsView
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val sensorProps = SensorLocationInternal("", 10, 100, 20)
.asFingerprintSensorProperties()
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
private lateinit var controllerOverlay: UdfpsControllerOverlay private lateinit var controllerOverlay: UdfpsControllerOverlay
@Before @Before
@@ -121,7 +129,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager, statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController, configurationController, systemClock, keyguardStateController,
unlockedScreenOffAnimationController, sensorProps, hbmProvider, REQUEST_ID, reason, unlockedScreenOffAnimationController, hbmProvider, REQUEST_ID, reason,
controllerCallback, onTouch, activityLaunchAnimator) controllerCallback, onTouch, activityLaunchAnimator)
block() block()
} }
@@ -148,12 +156,96 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test @Test
fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() } fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
// Sensor that's in the top left corner of the display in natural orientation.
val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
overlayParams = UdfpsOverlayParams(
sensorBounds,
DISPLAY_WIDTH,
DISPLAY_HEIGHT,
scaleFactor = 1f,
rotation
)
block()
}
@Test
fun showUdfpsOverlay_withRotation0() = withRotation(Surface.ROTATION_0) {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
eq(controllerOverlay.overlayView),
layoutParamsCaptor.capture()
)
// ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
}
}
@Test
fun showUdfpsOverlay_withRotation180() = withRotation(Surface.ROTATION_180) {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
eq(controllerOverlay.overlayView),
layoutParamsCaptor.capture()
)
// ROTATION_180 is not supported. Sensor should stay in the top left corner.
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
}
}
@Test
fun showUdfpsOverlay_withRotation90() = withRotation(Surface.ROTATION_90) {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
eq(controllerOverlay.overlayView),
layoutParamsCaptor.capture()
)
// Sensor should be in the bottom left corner in ROTATION_90.
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(DISPLAY_WIDTH - SENSOR_WIDTH)
assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
}
}
@Test
fun showUdfpsOverlay_withRotation270() = withRotation(Surface.ROTATION_270) {
withReason(REASON_AUTH_BP) {
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
eq(controllerOverlay.overlayView),
layoutParamsCaptor.capture()
)
// Sensor should be in the top right corner in ROTATION_270.
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(DISPLAY_HEIGHT - SENSOR_HEIGHT)
assertThat(lp.y).isEqualTo(0)
assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
}
}
private fun showUdfpsOverlay(isEnrollUseCase: Boolean = false) { private fun showUdfpsOverlay(isEnrollUseCase: Boolean = false) {
val didShow = controllerOverlay.show(udfpsController) val didShow = controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(eq(controllerOverlay.overlayView), any()) verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
verify(udfpsView).setHbmProvider(eq(hbmProvider)) verify(udfpsView).setHbmProvider(eq(hbmProvider))
verify(udfpsView).sensorProperties = eq(sensorProps)
verify(udfpsView).animationViewController = any() verify(udfpsView).animationViewController = any()
verify(udfpsView).addView(any()) verify(udfpsView).addView(any())
@@ -162,7 +254,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
assertThat(controllerOverlay.isHiding).isFalse() assertThat(controllerOverlay.isHiding).isFalse()
assertThat(controllerOverlay.overlayView).isNotNull() assertThat(controllerOverlay.overlayView).isNotNull()
if (isEnrollUseCase) { if (isEnrollUseCase) {
verify(udfpsEnrollView).updateSensorLocation(eq(sensorProps)) verify(udfpsEnrollView).updateSensorLocation(eq(overlayParams.sensorBounds))
assertThat(controllerOverlay.enrollHelper).isNotNull() assertThat(controllerOverlay.enrollHelper).isNotNull()
} else { } else {
assertThat(controllerOverlay.enrollHelper).isNull() assertThat(controllerOverlay.enrollHelper).isNull()
@@ -188,7 +280,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() } fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
private fun hideUdfpsOverlay() { private fun hideUdfpsOverlay() {
val didShow = controllerOverlay.show(udfpsController) val didShow = controllerOverlay.show(udfpsController, overlayParams)
val view = controllerOverlay.overlayView val view = controllerOverlay.overlayView
val didHide = controllerOverlay.hide() val didHide = controllerOverlay.hide()
@@ -209,13 +301,13 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test @Test
fun canNotReshow() = withReason(REASON_AUTH_BP) { fun canNotReshow() = withReason(REASON_AUTH_BP) {
assertThat(controllerOverlay.show(udfpsController)).isTrue() assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue()
assertThat(controllerOverlay.show(udfpsController)).isFalse() assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse()
} }
@Test @Test
fun forwardEnrollProgressEvents() = withReason(REASON_ENROLL_ENROLLING) { fun forwardEnrollProgressEvents() = withReason(REASON_ENROLL_ENROLLING) {
controllerOverlay.show(udfpsController) controllerOverlay.show(udfpsController, overlayParams)
with(EnrollListener(controllerOverlay)) { with(EnrollListener(controllerOverlay)) {
controllerOverlay.onEnrollmentProgress(/* remaining */20) controllerOverlay.onEnrollmentProgress(/* remaining */20)
@@ -228,7 +320,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test @Test
fun forwardEnrollHelpEvents() = withReason(REASON_ENROLL_ENROLLING) { fun forwardEnrollHelpEvents() = withReason(REASON_ENROLL_ENROLLING) {
controllerOverlay.show(udfpsController) controllerOverlay.show(udfpsController, overlayParams)
with(EnrollListener(controllerOverlay)) { with(EnrollListener(controllerOverlay)) {
controllerOverlay.onEnrollmentHelp() controllerOverlay.onEnrollmentHelp()
@@ -240,7 +332,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test @Test
fun forwardEnrollAcquiredEvents() = withReason(REASON_ENROLL_ENROLLING) { fun forwardEnrollAcquiredEvents() = withReason(REASON_ENROLL_ENROLLING) {
controllerOverlay.show(udfpsController) controllerOverlay.show(udfpsController, overlayParams)
with(EnrollListener(controllerOverlay)) { with(EnrollListener(controllerOverlay)) {
controllerOverlay.onEnrollmentProgress(/* remaining */ 1) controllerOverlay.onEnrollmentProgress(/* remaining */ 1)
@@ -261,7 +353,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) { fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) {
whenever(udfpsView.isIlluminationRequested).thenReturn(true) whenever(udfpsView.isIlluminationRequested).thenReturn(true)
controllerOverlay.show(udfpsController) controllerOverlay.show(udfpsController, overlayParams)
controllerOverlay.hide() controllerOverlay.hide()
verify(udfpsView).stopIllumination() verify(udfpsView).stopIllumination()
} }

View File

@@ -16,6 +16,9 @@
package com.android.systemui.biometrics; package com.android.systemui.biometrics;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@@ -28,11 +31,12 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.res.TypedArray; import android.graphics.Rect;
import android.hardware.biometrics.BiometricOverlayConstants; import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.SensorProperties;
@@ -50,6 +54,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
@@ -67,7 +72,6 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -124,8 +128,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock @Mock
private StatusBarStateController mStatusBarStateController; private StatusBarStateController mStatusBarStateController;
@Mock @Mock
private CentralSurfaces mCentralSurfaces;
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock @Mock
private DumpManager mDumpManager; private DumpManager mDumpManager;
@@ -174,18 +176,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
private UdfpsFpmOtherView mFpmOtherView; private UdfpsFpmOtherView mFpmOtherView;
@Mock @Mock
private UdfpsKeyguardView mKeyguardView; private UdfpsKeyguardView mKeyguardView;
private UdfpsAnimationViewController mUdfpsKeyguardViewController = private final UdfpsAnimationViewController mUdfpsKeyguardViewController =
mock(UdfpsKeyguardViewController.class); mock(UdfpsKeyguardViewController.class);
@Mock @Mock
private TypedArray mBrightnessValues;
@Mock
private TypedArray mBrightnessBacklight;
@Mock
private SystemUIDialogManager mSystemUIDialogManager; private SystemUIDialogManager mSystemUIDialogManager;
@Mock @Mock
private ActivityLaunchAnimator mActivityLaunchAnimator; private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock @Mock
private AlternateUdfpsTouchProvider mTouchProvider; private AlternateUdfpsTouchProvider mAlternateTouchProvider;
// Capture listeners so that they can be used to send events // Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor; @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -198,7 +196,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Before @Before
public void setUp() { public void setUp() {
setUpResources();
mExecution = new FakeExecution(); mExecution = new FakeExecution();
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)) when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
@@ -260,22 +257,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
mSystemUIDialogManager, mSystemUIDialogManager,
mLatencyTracker, mLatencyTracker,
mActivityLaunchAnimator, mActivityLaunchAnimator,
Optional.of(mTouchProvider)); Optional.of(mAlternateTouchProvider));
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue(); mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue(); mScreenObserver = mScreenObserverCaptor.getValue();
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams());
assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mSensorProps.sensorId);
}
private void setUpResources() {
when(mBrightnessValues.length()).thenReturn(2);
when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
when(mBrightnessBacklight.length()).thenReturn(2);
when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
} }
@Test @Test
@@ -301,7 +288,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN ACTION_DOWN is received // WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle(); downEvent.recycle();
@@ -362,7 +349,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN multiple touches are received // WHEN multiple touches are received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle(); downEvent.recycle();
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
@@ -395,7 +382,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler)); verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID); mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
@@ -403,6 +390,175 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mDisplayManager).unregisterDisplayListener(any()); verify(mDisplayManager).unregisterDisplayListener(any());
} }
@Test
public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception {
final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)};
final int[] displayWidth = new int[]{1080, 1440};
final int[] displayHeight = new int[]{1920, 2560};
final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0]);
for (int i1 = 0; i1 <= 1; ++i1)
for (int i2 = 0; i2 <= 1; ++i2)
for (int i3 = 0; i3 <= 1; ++i3)
for (int i4 = 0; i4 <= 1; ++i4)
for (int i5 = 0; i5 <= 1; ++i5) {
final UdfpsOverlayParams newParams = new UdfpsOverlayParams(sensorBounds[i1],
displayWidth[i2], displayHeight[i3], scaleFactor[i4], rotation[i5]);
if (newParams.equals(oldParams)) {
continue;
}
// Initialize the overlay with old parameters.
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, oldParams);
// Show the overlay.
reset(mWindowManager);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING,
mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(any(), any());
// Update overlay parameters.
reset(mWindowManager);
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, newParams);
mFgExecutor.runAllReady();
// Ensure the overlay was recreated.
verify(mWindowManager).removeView(any());
verify(mWindowManager).addView(any(), any());
}
}
@Test
public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception {
final Rect sensorBounds = new Rect(10, 10, 20, 20);
final int displayWidth = 1080;
final int displayHeight = 1920;
final float scaleFactor = 1f;
final int rotation = Surface.ROTATION_0;
// Initialize the overlay.
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
rotation));
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(any(), any());
// Update overlay with the same parameters.
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
rotation));
mFgExecutor.runAllReady();
// Ensure the overlay was not recreated.
verify(mWindowManager, never()).removeView(any());
}
private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor,
float major) {
MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
pp.id = 1;
MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
pc.x = x;
pc.y = y;
pc.touchMinor = minor;
pc.touchMajor = major;
return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp},
new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
}
@Test
public void onTouch_propagatesTouchInNativeOrientationAndResolution() throws RemoteException {
final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
final int displayWidth = 1080;
final int displayHeight = 1920;
final float scaleFactor = 0.75f; // This means the native resolution is 1440x2560.
final float touchMinor = 10f;
final float touchMajor = 20f;
// Expecting a touch at the very bottom right corner in native orientation and resolution.
final int expectedX = (int) (displayWidth / scaleFactor);
final int expectedY = (int) (displayHeight / scaleFactor);
final float expectedMinor = touchMinor / scaleFactor;
final float expectedMajor = touchMajor / scaleFactor;
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// Test ROTATION_0
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
Surface.ROTATION_0));
MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
// Test ROTATION_90
reset(mAlternateTouchProvider);
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
Surface.ROTATION_90));
event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
event = obtainMotionEvent(ACTION_MOVE, displayHeight, 0, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
// Test ROTATION_270
reset(mAlternateTouchProvider);
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
Surface.ROTATION_270));
event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
event = obtainMotionEvent(ACTION_MOVE, 0, displayWidth, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
// Test ROTATION_180
reset(mAlternateTouchProvider);
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
Surface.ROTATION_180));
// ROTATION_180 is not supported. It should be treated like ROTATION_0.
event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
}
@Test @Test
public void fingerDown() throws RemoteException { public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event // Configure UdfpsView to accept the ACTION_DOWN event
@@ -415,15 +571,17 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received // WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle(); downEvent.recycle();
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
// FIX THIS TEST
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
moveEvent.recycle(); moveEvent.recycle();
// THEN FingerprintManager is notified about onPointerDown // THEN FingerprintManager is notified about onPointerDown
verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
eq(0), eq(0), eq(0f), eq(0f)); eq(0f));
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(), verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat()); anyFloat(), anyFloat());
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
@@ -434,7 +592,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mOnIlluminatedRunnableCaptor.getValue().run(); mOnIlluminatedRunnableCaptor.getValue().run();
InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
inOrder.verify(mFingerprintManager).onUiReady( inOrder.verify(mFingerprintManager).onUiReady(
eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorProps.sensorId)); eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorId));
inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
} }
@@ -452,7 +610,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// AND onIlluminatedRunnable that notifies FingerprintManager is set // AND onIlluminatedRunnable that notifies FingerprintManager is set
verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
mOnIlluminatedRunnableCaptor.getValue().run(); mOnIlluminatedRunnableCaptor.getValue().run();
verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */); eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(), verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat()); anyFloat(), anyFloat());
@@ -573,7 +731,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN ACTION_DOWN is received // WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle(); downEvent.recycle();
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);

View File

@@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper import android.testing.TestableLooper
import android.testing.ViewUtils import android.testing.ViewUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Surface
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
@@ -69,9 +70,8 @@ class UdfpsViewTest : SysuiTestCase() {
com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0) com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
view.animationViewController = animationViewController view.animationViewController = animationViewController
view.sensorProperties = val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
SensorLocationInternal(DISPLAY_ID, SENSOR_X, SENSOR_Y, SENSOR_RADIUS) view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
.asFingerprintSensorProperties()
view.setHbmProvider(hbmProvider) view.setHbmProvider(hbmProvider)
ViewUtils.attachView(view) ViewUtils.attachView(view)
} }

View File

@@ -399,7 +399,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
/* resetLockoutRequiresHwToken */ false, /* resetLockoutRequiresHwToken */ false,
List.of(new SensorLocationInternal("" /* displayId */, List.of(new SensorLocationInternal("" /* displayId */,
(int) udfpsLocation.x, (int) udfpsLocation.y, radius))); (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
return new Pair(radius, udfpsLocation); return new Pair(radius, udfpsLocation);