Merge "Refactor UDFPS display logic and remove illumination" into tm-qpr-dev

This commit is contained in:
Ilya Matyukhin
2022-09-19 23:50:27 +00:00
committed by Android (Google) Code Review
21 changed files with 174 additions and 275 deletions

View File

@@ -315,17 +315,17 @@ public interface BiometricFingerprintConstants {
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
/** /**
* Whether the FingerprintAcquired message is a signal to turn off HBM * Whether the FingerprintAcquired message is a signal to disable the UDFPS display mode.
* We want to disable the UDFPS mode as soon as possible to conserve power and provide better
* UX. For example, prolonged high-brightness illumination of optical sensors can be unpleasant
* to the user, can cause long term display burn-in, and can drain the battery faster.
*/ */
static boolean shouldTurnOffHbm(@FingerprintAcquired int acquiredInfo) { static boolean shouldDisableUdfpsDisplayMode(@FingerprintAcquired int acquiredInfo) {
switch (acquiredInfo) { switch (acquiredInfo) {
case FINGERPRINT_ACQUIRED_START: case FINGERPRINT_ACQUIRED_START:
// Authentication just began // Keep the UDFPS mode because the authentication just began.
return false; return false;
case FINGERPRINT_ACQUIRED_GOOD: case FINGERPRINT_ACQUIRED_GOOD:
// Good image captured. Turn off HBM. Success/Reject comes after, which is when
// hideUdfpsOverlay will be called.
return true;
case FINGERPRINT_ACQUIRED_PARTIAL: case FINGERPRINT_ACQUIRED_PARTIAL:
case FINGERPRINT_ACQUIRED_INSUFFICIENT: case FINGERPRINT_ACQUIRED_INSUFFICIENT:
case FINGERPRINT_ACQUIRED_IMAGER_DIRTY: case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
@@ -334,11 +334,12 @@ public interface BiometricFingerprintConstants {
case FINGERPRINT_ACQUIRED_IMMOBILE: case FINGERPRINT_ACQUIRED_IMMOBILE:
case FINGERPRINT_ACQUIRED_TOO_BRIGHT: case FINGERPRINT_ACQUIRED_TOO_BRIGHT:
case FINGERPRINT_ACQUIRED_VENDOR: case FINGERPRINT_ACQUIRED_VENDOR:
// Bad image captured. Turn off HBM. Matcher will not run, so there's no need to // Disable the UDFPS mode because the image capture has finished. The overlay
// keep HBM on. // can be hidden later, once the authentication result arrives.
return true; return true;
case FINGERPRINT_ACQUIRED_UNKNOWN: case FINGERPRINT_ACQUIRED_UNKNOWN:
default: default:
// Keep the UDFPS mode in case of an unknown message.
return false; return false;
} }
} }

View File

@@ -4877,9 +4877,6 @@
--> -->
</array> </array>
<!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
<integer name="config_udfps_illumination_transition_ms">50</integer>
<!-- Indicates whether device has a power button fingerprint sensor. --> <!-- Indicates whether device has a power button fingerprint sensor. -->
<bool name="config_is_powerbutton_fps" translatable="false" >false</bool> <bool name="config_is_powerbutton_fps" translatable="false" >false</bool>

View File

@@ -2706,7 +2706,6 @@
<java-symbol type="bool" name="allow_test_udfps" /> <java-symbol type="bool" name="allow_test_udfps" />
<java-symbol type="array" name="config_udfps_sensor_props" /> <java-symbol type="array" name="config_udfps_sensor_props" />
<java-symbol type="array" name="config_sfps_sensor_props" /> <java-symbol type="array" name="config_sfps_sensor_props" />
<java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
<java-symbol type="bool" name="config_is_powerbutton_fps" /> <java-symbol type="bool" name="config_is_powerbutton_fps" />
<java-symbol type="array" name="config_udfps_enroll_stage_thresholds" /> <java-symbol type="array" name="config_udfps_enroll_stage_thresholds" />
<java-symbol type="array" name="config_sfps_enroll_stage_thresholds" /> <java-symbol type="array" name="config_sfps_enroll_stage_thresholds" />

View File

@@ -291,7 +291,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
} }
}); });
mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation); mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
mUdfpsController.setHalControlsIllumination(mUdfpsProps.get(0).halControlsIllumination);
mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect(); mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
} }

View File

@@ -264,7 +264,7 @@ class AuthRippleController @Inject constructor(
acquireInfo: Int acquireInfo: Int
) { ) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT && if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
BiometricFingerprintConstants.shouldTurnOffHbm(acquireInfo) && BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquireInfo) &&
acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) { acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
// received an 'acquiredBad' message, so immediately retract // received an 'acquiredBad' message, so immediately retract
mView.retractDwellRipple() mView.retractDwellRipple()

View File

@@ -54,13 +54,13 @@ public abstract class UdfpsAnimationView extends FrameLayout {
getDrawable().onSensorRectUpdated(bounds); getDrawable().onSensorRectUpdated(bounds);
} }
void onIlluminationStarting() { void onDisplayConfiguring() {
getDrawable().setIlluminationShowing(true); getDrawable().setDisplayConfigured(true);
getDrawable().invalidateSelf(); getDrawable().invalidateSelf();
} }
void onIlluminationStopped() { void onDisplayUnconfigured() {
getDrawable().setIlluminationShowing(false); getDrawable().setDisplayConfigured(false);
getDrawable().invalidateSelf(); getDrawable().invalidateSelf();
} }

View File

@@ -31,10 +31,10 @@ import java.io.PrintWriter
/** /**
* Handles: * Handles:
* 1. registering for listeners when its view is attached and unregistering on view detached * 1. registering for listeners when its view is attached and unregistering on view detached
* 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide * 2. pausing UDFPS when FingerprintManager may still be running but we temporarily want to hide
* the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth) * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
* 3. sending events to its view including: * 3. sending events to its view including:
* - illumination events * - enabling and disabling of the UDFPS display mode
* - sensor position changes * - sensor position changes
* - doze time event * - doze time event
*/ */
@@ -167,19 +167,20 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
} }
/** /**
* Udfps has started illuminating and the fingerprint manager is working on authenticating. * The display began transitioning into the UDFPS mode and the fingerprint manager started
* authenticating.
*/ */
fun onIlluminationStarting() { fun onDisplayConfiguring() {
view.onIlluminationStarting() view.onDisplayConfiguring()
view.postInvalidate() view.postInvalidate()
} }
/** /**
* Udfps has stopped illuminating and the fingerprint manager is no longer attempting to * The display transitioned away from the UDFPS mode and the fingerprint manager stopped
* authenticate. * authenticating.
*/ */
fun onIlluminationStopped() { fun onDisplayUnconfigured() {
view.onIlluminationStopped() view.onDisplayUnconfigured()
view.postInvalidate() view.postInvalidate()
} }

View File

@@ -86,7 +86,7 @@ import kotlin.Unit;
/** /**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events, * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and coordinates triggering of the high-brightness mode (HBM). * and toggles the UDFPS display mode.
* *
* Note that the current architecture is designed so that a single {@link UdfpsController} * Note that the current architecture is designed so that a single {@link UdfpsController}
* controls/manages all UDFPS sensors. In other words, a single controller is registered with * controls/manages all UDFPS sensors. In other words, a single controller is registered with
@@ -123,7 +123,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final PowerManager mPowerManager; @NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager; @NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Nullable private final UdfpsHbmProvider mHbmProvider; @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode;
@NonNull private final ConfigurationController mConfigurationController; @NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock; @NonNull private final SystemClock mSystemClock;
@NonNull private final UnlockedScreenOffAnimationController @NonNull private final UnlockedScreenOffAnimationController
@@ -135,7 +135,6 @@ public class UdfpsController implements DozeReceiver {
// 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 int mSensorId; @VisibleForTesting int mSensorId;
private boolean mHalControlsIllumination;
@VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams(); @VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this. // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation; @Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@@ -147,10 +146,9 @@ public class UdfpsController implements DozeReceiver {
private int mActivePointerId = -1; private int mActivePointerId = -1;
// The timestamp of the most recent touch log. // The timestamp of the most recent touch log.
private long mTouchLogTime; private long mTouchLogTime;
// Sensor has a capture (good or bad) for this touch. Do not need to illuminate for this // Sensor has a capture (good or bad) for this touch. No need to enable the UDFPS display mode
// particular touch event anymore. In other words, do not illuminate until user lifts and // anymore for this particular touch event. In other words, do not enable the UDFPS mode until
// touches the sensor area again. // the user touches the sensor area again.
// TODO: We should probably try to make touch/illumination things more of a FSM
private boolean mAcquiredReceived; private boolean mAcquiredReceived;
// The current request from FingerprintService. Null if no current request. // The current request from FingerprintService. Null if no current request.
@@ -211,8 +209,8 @@ public class UdfpsController implements DozeReceiver {
mKeyguardUpdateMonitor, mDialogManager, mDumpManager, mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
mLockscreenShadeTransitionController, mConfigurationController, mLockscreenShadeTransitionController, mConfigurationController,
mSystemClock, mKeyguardStateController, mSystemClock, mKeyguardStateController,
mUnlockedScreenOffAnimationController, mHalControlsIllumination, mUnlockedScreenOffAnimationController,
mHbmProvider, requestId, reason, callback, mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event, (view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator))); fromUdfpsView), mActivityLaunchAnimator)));
} }
@@ -236,7 +234,7 @@ public class UdfpsController implements DozeReceiver {
int sensorId, int sensorId,
@BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
) { ) {
if (BiometricFingerprintConstants.shouldTurnOffHbm(acquiredInfo)) { if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) {
boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD; boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
mFgExecutor.execute(() -> { mFgExecutor.execute(() -> {
if (mOverlay == null) { if (mOverlay == null) {
@@ -247,7 +245,7 @@ public class UdfpsController implements DozeReceiver {
mAcquiredReceived = true; mAcquiredReceived = true;
final UdfpsView view = mOverlay.getOverlayView(); final UdfpsView view = mOverlay.getOverlayView();
if (view != null) { if (view != null) {
view.stopIllumination(); // turn off HBM view.unconfigureDisplay();
} }
if (acquiredGood) { if (acquiredGood) {
mOverlay.onAcquiredGood(); mOverlay.onAcquiredGood();
@@ -292,7 +290,7 @@ public class UdfpsController implements DozeReceiver {
/** /**
* Updates the overlay parameters and reconstructs or redraws the overlay, if necessary. * Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
* *
* @param sensorId sensor for which the overlay is getting updated. * @param sensorId sensor for which the overlay is getting updated.
* @param overlayParams See {@link UdfpsOverlayParams}. * @param overlayParams See {@link UdfpsOverlayParams}.
*/ */
public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) { public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
@@ -321,11 +319,6 @@ public class UdfpsController implements DozeReceiver {
mAuthControllerUpdateUdfpsLocation = r; mAuthControllerUpdateUdfpsLocation = r;
} }
// TODO(b/229290039): UDFPS controller should manage its properties on its own. Remove this.
public void setHalControlsIllumination(boolean value) {
mHalControlsIllumination = value;
}
/** /**
* 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.
@@ -369,8 +362,8 @@ public class UdfpsController implements DozeReceiver {
} }
/** /**
* @param x coordinate * @param x coordinate
* @param y coordinate * @param y coordinate
* @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else, * @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else,
* calculate from the display dimensions in portrait orientation * calculate from the display dimensions in portrait orientation
*/ */
@@ -423,7 +416,7 @@ public class UdfpsController implements DozeReceiver {
} }
final UdfpsView udfpsView = mOverlay.getOverlayView(); final UdfpsView udfpsView = mOverlay.getOverlayView();
final boolean isIlluminationRequested = udfpsView.isIlluminationRequested(); final boolean isDisplayConfigured = udfpsView.isDisplayConfigured();
boolean handled = false; boolean handled = false;
switch (event.getActionMasked()) { switch (event.getActionMasked()) {
case MotionEvent.ACTION_OUTSIDE: case MotionEvent.ACTION_OUTSIDE:
@@ -507,7 +500,7 @@ public class UdfpsController implements DozeReceiver {
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b", "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold); minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime; final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
if (!isIlluminationRequested && !mAcquiredReceived if (!isDisplayConfigured && !mAcquiredReceived
&& !exceedsVelocityThreshold) { && !exceedsVelocityThreshold) {
final float scale = mOverlayParams.getScaleFactor(); final float scale = mOverlayParams.getScaleFactor();
@@ -598,7 +591,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull VibratorHelper vibrator, @NonNull VibratorHelper vibrator,
@NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@NonNull UdfpsShell udfpsShell, @NonNull UdfpsShell udfpsShell,
@NonNull Optional<UdfpsHbmProvider> hbmProvider, @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode,
@NonNull KeyguardStateController keyguardStateController, @NonNull KeyguardStateController keyguardStateController,
@NonNull DisplayManager displayManager, @NonNull DisplayManager displayManager,
@Main Handler mainHandler, @Main Handler mainHandler,
@@ -630,7 +623,7 @@ public class UdfpsController implements DozeReceiver {
mPowerManager = powerManager; mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager; mAccessibilityManager = accessibilityManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mHbmProvider = hbmProvider.orElse(null); mUdfpsDisplayMode = udfpsDisplayMode.orElse(null);
screenLifecycle.addObserver(mScreenObserver); screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
mConfigurationController = configurationController; mConfigurationController = configurationController;
@@ -804,15 +797,14 @@ public class UdfpsController implements DozeReceiver {
} }
/** /**
* Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before * Cancel UDFPS affordances - ability to hide the UDFPS overlay before the user explicitly
* user explicitly lifts their finger. Generally, this should be called whenever udfps fails * lifts their finger. Generally, this should be called on errors in the authentication flow.
* or errors.
* *
* The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give * The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give
* ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually. * ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually.
* This should be called when authentication either succeeds or fails. Failing to cancel the * This should be called when authentication either succeeds or fails. Failing to cancel the
* scan will leave the screen in high brightness mode and will show the HbmSurfaceView until * scan will leave the display in the UDFPS mode until the user lifts their finger. On optical
* the user lifts their finger. * sensors, this can result in illumination persisting for longer than necessary.
*/ */
void onCancelUdfps() { void onCancelUdfps() {
if (mOverlay != null && mOverlay.getOverlayView() != null) { if (mOverlay != null && mOverlay.getOverlayView() != null) {
@@ -874,7 +866,7 @@ public class UdfpsController implements DozeReceiver {
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.configureDisplay(() -> {
if (mAlternateTouchProvider != null) { if (mAlternateTouchProvider != null) {
mBiometricExecutor.execute(() -> { mBiometricExecutor.execute(() -> {
mAlternateTouchProvider.onUiReady(); mAlternateTouchProvider.onUiReady();
@@ -914,8 +906,8 @@ public class UdfpsController implements DozeReceiver {
} }
} }
mOnFingerDown = false; mOnFingerDown = false;
if (view.isIlluminationRequested()) { if (view.isDisplayConfigured()) {
view.stopIllumination(); view.unconfigureDisplay();
} }
} }

View File

@@ -77,8 +77,7 @@ 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 halControlsIllumination: Boolean, private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
private var hbmProvider: UdfpsHbmProvider,
val requestId: Long, val requestId: Long,
@ShowReason val requestReason: Int, @ShowReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback, private val controllerCallback: IUdfpsOverlayControllerCallback,
@@ -102,8 +101,8 @@ 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 = flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
(Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH) WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
// Avoid announcing window title. // Avoid announcing window title.
accessibilityTitle = " " accessibilityTitle = " "
@@ -140,8 +139,7 @@ class UdfpsControllerOverlay(
R.layout.udfps_view, null, false R.layout.udfps_view, null, false
) as UdfpsView).apply { ) as UdfpsView).apply {
overlayParams = params overlayParams = params
halControlsIllumination = this@UdfpsControllerOverlay.halControlsIllumination setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
setHbmProvider(hbmProvider)
val animation = inflateUdfpsAnimation(this, controller) val animation = inflateUdfpsAnimation(this, controller)
if (animation != null) { if (animation != null) {
animation.init() animation.init()
@@ -250,8 +248,8 @@ class UdfpsControllerOverlay(
val wasShowing = isShowing val wasShowing = isShowing
overlayView?.apply { overlayView?.apply {
if (isIlluminationRequested) { if (isDisplayConfigured) {
stopIllumination() unconfigureDisplay()
} }
windowManager.removeView(this) windowManager.removeView(this)
setOnTouchListener(null) setOnTouchListener(null)

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.biometrics;
import android.annotation.Nullable;
/**
* Interface for toggling the optimal display mode for the under-display fingerprint sensor
* (UDFPS). For example, the implementation might change the refresh rate and activate a
* high-brightness mode.
*/
public interface UdfpsDisplayModeProvider {
/**
* Enables the optimal display mode for UDFPS. The mode will persist until
* {@link #disable(Runnable)} is called.
*
* This call must be made from the UI thread. The callback, if provided, will also be invoked
* from the UI thread.
*
* @param onEnabled A runnable that will be executed once the mode is enabled.
*/
void enable(@Nullable Runnable onEnabled);
/**
* Disables the mode that was enabled by {@link #enable(Runnable)}.
*
* The call must be made from the UI thread. The callback, if provided, will also be invoked
* from the UI thread.
*
* @param onDisabled A runnable that will be executed once mode is disabled.
*/
void disable(@Nullable Runnable onDisabled);
}

View File

@@ -51,7 +51,7 @@ abstract class UdfpsDrawable(
invalidateSelf() invalidateSelf()
} }
var isIlluminationShowing: Boolean = false var isDisplayConfigured: Boolean = false
set(showing) { set(showing) {
if (field == showing) { if (field == showing) {
return return

View File

@@ -197,7 +197,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
@Override @Override
public void draw(@NonNull Canvas canvas) { public void draw(@NonNull Canvas canvas) {
if (isIlluminationShowing()) { if (isDisplayConfigured()) {
return; return;
} }

View File

@@ -23,7 +23,7 @@ import android.graphics.Canvas
*/ */
class UdfpsFpDrawable(context: Context) : UdfpsDrawable(context) { class UdfpsFpDrawable(context: Context) : UdfpsDrawable(context) {
override fun draw(canvas: Canvas) { override fun draw(canvas: Canvas) {
if (isIlluminationShowing) { if (isDisplayConfigured) {
return return
} }
fingerprintDrawable.draw(canvas) fingerprintDrawable.draw(canvas)

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.biometrics;
import android.annotation.Nullable;
/**
* Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
* enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
* illumination is no longer necessary.
*/
public interface UdfpsHbmProvider {
/**
* UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
*
* This method is a no-op when some type of HBM is already enabled.
*
* This method must be called from the UI thread. The callback, if provided, will also be
* invoked from the UI thread.
*
* @param onHbmEnabled A runnable that will be executed once HBM is enabled.
*
* TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
* This only makes sense now because vendor code may rely on the side effects of enableHbm.
*/
void enableHbm(boolean halControlsIllumination, @Nullable Runnable onHbmEnabled);
/**
* UdfpsView will call this to disable HBM when illumination is no longer needed.
*
* This method will disable HBM if HBM is enabled. Otherwise, if HBM is already disabled,
* this method is a no-op.
*
* The call must be made from the UI thread. The callback, if provided, will also be invoked
* from the UI thread.
*
* @param onHbmDisabled A runnable that will be executed once HBM is disabled.
*/
void disableHbm(@Nullable Runnable onHbmDisabled);
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.biometrics;
import android.annotation.Nullable;
/**
* Interface that should be implemented by UI's that need to coordinate user touches,
* views/animations, and modules that start/stop display illumination.
*/
interface UdfpsIlluminator {
/**
* @param hbmProvider Invoked when HBM should be enabled or disabled.
*/
void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider);
/**
* Invoked when illumination should start.
* @param onIlluminatedRunnable Invoked when the display has been illuminated.
*/
void startIllumination(@Nullable Runnable onIlluminatedRunnable);
/**
* Invoked when illumination should end.
*/
void stopIllumination();
}

View File

@@ -101,11 +101,11 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
} }
@Override @Override
void onIlluminationStarting() { void onDisplayConfiguring() {
} }
@Override @Override
void onIlluminationStopped() { void onDisplayUnconfigured() {
} }
@Override @Override

View File

@@ -36,12 +36,12 @@ private const val TAG = "UdfpsView"
class UdfpsView( class UdfpsView(
context: Context, context: Context,
attrs: AttributeSet? attrs: AttributeSet?
) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator { ) : FrameLayout(context, attrs), DozeReceiver {
// sensorRect may be bigger than the sensor. True sensor dimensions are defined in // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
// overlayParams.sensorBounds // overlayParams.sensorBounds
private val sensorRect = RectF() private val sensorRect = RectF()
private var hbmProvider: UdfpsHbmProvider? = null private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
private val debugTextPaint = Paint().apply { private val debugTextPaint = Paint().apply {
isAntiAlias = true isAntiAlias = true
color = Color.BLUE color = Color.BLUE
@@ -56,19 +56,12 @@ class UdfpsView(
a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f) a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f)
} }
private val onIlluminatedDelayMs = context.resources.getInteger(
com.android.internal.R.integer.config_udfps_illumination_transition_ms
).toLong()
/** 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
/** Parameters that affect the position and size of the overlay. */ /** Parameters that affect the position and size of the overlay. */
var overlayParams = UdfpsOverlayParams() var overlayParams = UdfpsOverlayParams()
/** Whether the HAL is responsible for enabling and disabling of LHBM. */
var halControlsIllumination: Boolean = true
/** Debug message. */ /** Debug message. */
var debugMessage: String? = null var debugMessage: String? = null
set(value) { set(value) {
@@ -76,12 +69,12 @@ class UdfpsView(
postInvalidate() postInvalidate()
} }
/** When [startIllumination] has been called but not stopped via [stopIllumination]. */ /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
var isIlluminationRequested: Boolean = false var isDisplayConfigured: Boolean = false
private set private set
override fun setHbmProvider(provider: UdfpsHbmProvider?) { fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
hbmProvider = provider mUdfpsDisplayMode = udfpsDisplayModeProvider
} }
// Don't propagate any touch events to the child views. // Don't propagate any touch events to the child views.
@@ -124,7 +117,7 @@ class UdfpsView(
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
if (!isIlluminationRequested) { if (!isDisplayConfigured) {
if (!debugMessage.isNullOrEmpty()) { if (!debugMessage.isNullOrEmpty()) {
canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint) canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
} }
@@ -147,36 +140,15 @@ class UdfpsView(
!(animationViewController?.shouldPauseAuth() ?: false) !(animationViewController?.shouldPauseAuth() ?: false)
} }
/** fun configureDisplay(onDisplayConfigured: Runnable) {
* Start and run [onIlluminatedRunnable] when the first illumination frame reaches the panel. isDisplayConfigured = true
*/ animationViewController?.onDisplayConfiguring()
override fun startIllumination(onIlluminatedRunnable: Runnable?) { mUdfpsDisplayMode?.enable(onDisplayConfigured)
isIlluminationRequested = true
animationViewController?.onIlluminationStarting()
doIlluminate(onIlluminatedRunnable)
} }
private fun doIlluminate(onIlluminatedRunnable: Runnable?) { fun unconfigureDisplay() {
// TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense. isDisplayConfigured = false
// This only makes sense now because vendor code may rely on the side effects of enableHbm. animationViewController?.onDisplayUnconfigured()
hbmProvider?.enableHbm(halControlsIllumination) { mUdfpsDisplayMode?.disable(null /* onDisabled */)
if (onIlluminatedRunnable != null) {
if (halControlsIllumination) {
onIlluminatedRunnable.run()
} else {
// No framework API can reliably tell when a frame reaches the panel. A timeout
// is the safest solution.
postDelayed(onIlluminatedRunnable, onIlluminatedDelayMs)
}
} else {
Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null")
}
}
}
override fun stopIllumination() {
isIlluminationRequested = false
animationViewController?.onIlluminationStopped()
hbmProvider?.disableHbm(null /* onHbmDisabled */)
} }
} }

View File

@@ -30,7 +30,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule; import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider; import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
import com.android.systemui.biometrics.UdfpsHbmProvider; import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule; import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.classifier.FalsingModule; import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule; import com.android.systemui.controls.dagger.ControlsModule;
@@ -197,7 +197,7 @@ public abstract class SystemUIModule {
abstract CentralSurfaces optionalCentralSurfaces(); abstract CentralSurfaces optionalCentralSurfaces();
@BindsOptionalOf @BindsOptionalOf
abstract UdfpsHbmProvider optionalUdfpsHbmProvider(); abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
@BindsOptionalOf @BindsOptionalOf
abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider(); abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();

View File

@@ -17,23 +17,13 @@
package com.android.systemui.biometrics package com.android.systemui.biometrics
import android.graphics.Rect import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricOverlayConstants.*
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
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
import android.testing.TestableLooper.RunWithLooper import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater import android.view.*
import android.view.MotionEvent
import android.view.View
import android.view.Surface
import android.view.Surface.Rotation import android.view.Surface.Rotation
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor
@@ -65,7 +55,6 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
private const val HAL_CONTROLS_ILLUMINATION = true
private const val REQUEST_ID = 2L private const val REQUEST_ID = 2L
// Dimensions for the current display resolution. // Dimensions for the current display resolution.
@@ -95,8 +84,9 @@ 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 private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController @Mock private lateinit var unlockedScreenOffAnimationController:
@Mock private lateinit var hbmProvider: UdfpsHbmProvider UnlockedScreenOffAnimationController
@Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
@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
@@ -130,8 +120,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager, statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController, configurationController, systemClock, keyguardStateController,
unlockedScreenOffAnimationController, HAL_CONTROLS_ILLUMINATION, hbmProvider, unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator) controllerCallback, onTouch, activityLaunchAnimator
)
block() block()
} }
@@ -246,7 +237,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
val didShow = controllerOverlay.show(udfpsController, overlayParams) 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).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
verify(udfpsView).animationViewController = any() verify(udfpsView).animationViewController = any()
verify(udfpsView).addView(any()) verify(udfpsView).addView(any())
@@ -351,12 +342,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
} }
@Test @Test
fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) { fun unconfigureDisplayOnHide() = withReason(REASON_AUTH_BP) {
whenever(udfpsView.isIlluminationRequested).thenReturn(true) whenever(udfpsView.isDisplayConfigured).thenReturn(true)
controllerOverlay.show(udfpsController, overlayParams) controllerOverlay.show(udfpsController, overlayParams)
controllerOverlay.hide() controllerOverlay.hide()
verify(udfpsView).stopIllumination() verify(udfpsView).unconfigureDisplay()
} }
@Test @Test

View File

@@ -125,7 +125,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock @Mock
private WindowManager mWindowManager; private WindowManager mWindowManager;
@Mock @Mock
private UdfpsHbmProvider mHbmProvider; private UdfpsDisplayModeProvider mDisplayModeProvider;
@Mock @Mock
private StatusBarStateController mStatusBarStateController; private StatusBarStateController mStatusBarStateController;
@Mock @Mock
@@ -193,7 +193,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
private IUdfpsOverlayController mOverlayController; private IUdfpsOverlayController mOverlayController;
@Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
@Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor; @Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
@Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor; @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
@Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor; @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private ScreenLifecycle.Observer mScreenObserver; private ScreenLifecycle.Observer mScreenObserver;
@@ -256,7 +256,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mVibrator, mVibrator,
mUdfpsHapticsSimulator, mUdfpsHapticsSimulator,
mUdfpsShell, mUdfpsShell,
Optional.of(mHbmProvider), Optional.of(mDisplayModeProvider),
mKeyguardStateController, mKeyguardStateController,
mDisplayManager, mDisplayManager,
mHandler, mHandler,
@@ -506,7 +506,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
final float expectedMajor = touchMajor / scaleFactor; final float expectedMajor = touchMajor / scaleFactor;
// Configure UdfpsView to accept the ACTION_DOWN event // Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isIlluminationRequested()).thenReturn(false); when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay. // Show the overlay.
@@ -584,7 +584,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@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
when(mUdfpsView.isIlluminationRequested()).thenReturn(false); when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
@@ -611,12 +611,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
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));
// AND illumination begins // AND display configuration begins
verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID)); verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
// AND onIlluminatedRunnable notifies FingerprintManager about onUiReady // AND onDisplayConfigured notifies FingerprintManager about onUiReady
mOnIlluminatedRunnableCaptor.getValue().run(); mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady(); mBiometricsExecutor.runAllReady();
InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker); InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
inOrder.verify(mAlternateTouchProvider).onUiReady(); inOrder.verify(mAlternateTouchProvider).onUiReady();
@@ -634,10 +634,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN fingerprint is requested because of AOD interrupt // WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
// THEN illumination begins // THEN display configuration begins
// AND onIlluminatedRunnable that notifies FingerprintManager is set // AND onDisplayConfigured notifies FingerprintManager about onUiReady
verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
mOnIlluminatedRunnableCaptor.getValue().run(); mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady(); mBiometricsExecutor.runAllReady();
verify(mAlternateTouchProvider).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 */);
@@ -655,11 +655,11 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
when(mUdfpsView.isIlluminationRequested()).thenReturn(true); when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it is cancelled // WHEN it is cancelled
mUdfpsController.onCancelUdfps(); mUdfpsController.onCancelUdfps();
// THEN the illumination is hidden // THEN the display is unconfigured
verify(mUdfpsView).stopIllumination(); verify(mUdfpsView).unconfigureDisplay();
} }
@Test @Test
@@ -672,12 +672,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
when(mUdfpsView.isIlluminationRequested()).thenReturn(true); when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it times out // WHEN it times out
mFgExecutor.advanceClockToNext(); mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady(); mFgExecutor.runAllReady();
// THEN the illumination is hidden // THEN the display is unconfigured
verify(mUdfpsView).stopIllumination(); verify(mUdfpsView).unconfigureDisplay();
} }
@Test @Test
@@ -692,8 +692,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
// THEN no illumination because screen is off // THEN display doesn't get configured because it's off
verify(mUdfpsView, never()).startIllumination(any()); verify(mUdfpsView, never()).configureDisplay(any());
} }
@Test @Test
@@ -709,14 +709,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
// THEN no illumination because screen is off // THEN display doesn't get configured because it's off
verify(mUdfpsView, never()).startIllumination(any()); verify(mUdfpsView, never()).configureDisplay(any());
} }
@Test @Test
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException { public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event // Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isIlluminationRequested()).thenReturn(false); when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration enabled // GIVEN that the overlay is showing and a11y touch exploration enabled
@@ -751,7 +751,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test @Test
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event // Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isIlluminationRequested()).thenReturn(false); when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled // GIVEN that the overlay is showing and a11y touch exploration NOT enabled

View File

@@ -36,13 +36,12 @@ 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.ArgumentMatchers.anyBoolean
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.nullable import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private const val SENSOR_X = 50 private const val SENSOR_X = 50
private const val SENSOR_Y = 250 private const val SENSOR_Y = 250
@@ -57,7 +56,7 @@ class UdfpsViewTest : SysuiTestCase() {
var rule = MockitoJUnit.rule() var rule = MockitoJUnit.rule()
@Mock @Mock
lateinit var hbmProvider: UdfpsHbmProvider lateinit var hbmProvider: UdfpsDisplayModeProvider
@Mock @Mock
lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView> lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
@@ -66,13 +65,11 @@ class UdfpsViewTest : SysuiTestCase() {
@Before @Before
fun setup() { fun setup() {
context.setTheme(R.style.Theme_AppCompat) context.setTheme(R.style.Theme_AppCompat)
context.orCreateTestableResources.addOverride(
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
val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0) view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
view.setHbmProvider(hbmProvider) view.setUdfpsDisplayModeProvider(hbmProvider)
ViewUtils.attachView(view) ViewUtils.attachView(view)
} }
@@ -143,27 +140,27 @@ class UdfpsViewTest : SysuiTestCase() {
@Test @Test
fun startAndStopIllumination() { fun startAndStopIllumination() {
val onDone: Runnable = mock() val onDone: Runnable = mock()
view.startIllumination(onDone) view.configureDisplay(onDone)
val illuminator = withArgCaptor<Runnable> { val illuminator = withArgCaptor<Runnable> {
verify(hbmProvider).enableHbm(anyBoolean(), capture()) verify(hbmProvider).enable(capture())
} }
assertThat(view.isIlluminationRequested).isTrue() assertThat(view.isDisplayConfigured).isTrue()
verify(animationViewController).onIlluminationStarting() verify(animationViewController).onDisplayConfiguring()
verify(animationViewController, never()).onIlluminationStopped() verify(animationViewController, never()).onDisplayUnconfigured()
verify(onDone, never()).run() verify(onDone, never()).run()
// fake illumination event // fake illumination event
illuminator.run() illuminator.run()
waitForLooper() waitForLooper()
verify(onDone).run() verify(onDone).run()
verify(hbmProvider, never()).disableHbm(any()) verify(hbmProvider, never()).disable(any())
view.stopIllumination() view.unconfigureDisplay()
assertThat(view.isIlluminationRequested).isFalse() assertThat(view.isDisplayConfigured).isFalse()
verify(animationViewController).onIlluminationStopped() verify(animationViewController).onDisplayUnconfigured()
verify(hbmProvider).disableHbm(nullable(Runnable::class.java)) verify(hbmProvider).disable(nullable(Runnable::class.java))
} }
private fun waitForLooper() = TestableLooper.get(this).processAllMessages() private fun waitForLooper() = TestableLooper.get(this).processAllMessages()