Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +48 −179 Original line number Diff line number Diff line Loading @@ -22,29 +22,22 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RectF; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.MathUtils; import android.util.Spline; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; Loading @@ -53,10 +46,6 @@ import com.android.systemui.doze.DozeReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.settings.SystemSettings; import java.io.FileWriter; import java.io.IOException; import javax.inject.Inject; Loading @@ -73,16 +62,13 @@ import javax.inject.Inject; */ @SuppressWarnings("deprecation") @SysUISingleton public class UdfpsController implements DozeReceiver { public class UdfpsController implements UdfpsView.HbmCallback, DozeReceiver { private static final String TAG = "UdfpsController"; // Gamma approximation for the sRGB color space. private static final float DISPLAY_GAMMA = 2.2f; private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; private final Context mContext; private final FingerprintManager mFingerprintManager; private final WindowManager mWindowManager; private final SystemSettings mSystemSettings; private final DelayableExecutor mFgExecutor; private final StatusBarStateController mStatusBarStateController; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple Loading @@ -90,23 +76,6 @@ public class UdfpsController implements DozeReceiver { @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; private final WindowManager.LayoutParams mCoreLayoutParams; private final UdfpsView mView; // Debugfs path to control the high-brightness mode. private final String mHbmPath; private final String mHbmEnableCommand; private final String mHbmDisableCommand; private final boolean mHbmSupported; // Brightness in nits in the high-brightness mode. private final float mMaxNits; // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to a // brightness in nits. private final Spline mBacklightToNitsSpline; // A spline mapping from a value in nits to a backlight value of a hypothetical panel whose // maximum backlight value corresponds to our panel's high-brightness mode. // The output is normalized to the range [0, 1.0]. private Spline mNitsToHbmBacklightSpline; // Default non-HBM backlight value normalized to the range [0, 1.0]. Used as a fallback when the // actual brightness value cannot be retrieved. private final float mDefaultBrightness; // Indicates whether the overlay is currently showing. Even if it has been requested, it might // not be showing. private boolean mIsOverlayShowing; Loading Loading @@ -152,7 +121,7 @@ public class UdfpsController implements DozeReceiver { @SuppressLint("ClickableViewAccessibility") private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> { UdfpsView view = (UdfpsView) v; final boolean isFingerDown = view.isShowScrimAndDot(); final boolean isFingerDown = view.isIlluminationRequested(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: Loading Loading @@ -183,9 +152,7 @@ public class UdfpsController implements DozeReceiver { @Main Resources resources, LayoutInflater inflater, @Nullable FingerprintManager fingerprintManager, DisplayManager displayManager, WindowManager windowManager, SystemSettings systemSettings, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, @NonNull ScrimController scrimController) { Loading @@ -194,7 +161,6 @@ public class UdfpsController implements DozeReceiver { // fingerprint manager should never be null. mFingerprintManager = checkNotNull(fingerprintManager); mWindowManager = windowManager; mSystemSettings = systemSettings; mFgExecutor = fgExecutor; mStatusBarStateController = statusBarStateController; Loading @@ -211,72 +177,18 @@ public class UdfpsController implements DozeReceiver { PixelFormat.TRANSLUCENT); mCoreLayoutParams.setTitle(TAG); mCoreLayoutParams.setFitInsetsTypes(0); mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mCoreLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false); mView.setSensorProperties(mSensorProps); mView.setHbmCallback(this); mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path); mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command); mHbmDisableCommand = resources.getString(R.string.udfps_hbm_disable_command); mHbmSupported = !TextUtils.isEmpty(mHbmPath); mView.setHbmSupported(mHbmSupported); scrimController.addScrimChangedListener(mView); statusBarStateController.addCallback(mView); // This range only consists of the minimum and maximum values, which only cover // non-high-brightness mode. float[] nitsRange = toFloatArray(resources.obtainTypedArray( com.android.internal.R.array.config_screenBrightnessNits)); if (nitsRange.length < 2) { throw new IllegalArgumentException( String.format("nitsRange.length: %d. Must be >= 2", nitsRange.length)); } // The last value of this range corresponds to the high-brightness mode. float[] nitsAutoBrightnessValues = toFloatArray(resources.obtainTypedArray( com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); if (nitsAutoBrightnessValues.length < 2) { throw new IllegalArgumentException( String.format("nitsAutoBrightnessValues.length: %d. Must be >= 2", nitsAutoBrightnessValues.length)); } mMaxNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1]; float[] hbmNitsRange = nitsRange.clone(); hbmNitsRange[hbmNitsRange.length - 1] = mMaxNits; // This range only consists of the minimum and maximum backlight values, which only apply // in non-high-brightness mode. float[] normalizedBacklightRange = normalizeBacklightRange( resources.getIntArray( com.android.internal.R.array.config_screenBrightnessBacklight)); if (normalizedBacklightRange.length < 2) { throw new IllegalArgumentException( String.format("normalizedBacklightRange.length: %d. Must be >= 2", normalizedBacklightRange.length)); } if (normalizedBacklightRange.length != nitsRange.length) { throw new IllegalArgumentException( "normalizedBacklightRange.length != nitsRange.length"); } mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange); mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange); mDefaultBrightness = obtainDefaultBrightness(mContext); // TODO(b/160025856): move to the "dump" method. Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0], nitsRange[nitsRange.length - 1])); Log.v(TAG, String.format("ctor | mHbmNitsRange: [%f, %f]", hbmNitsRange[0], hbmNitsRange[hbmNitsRange.length - 1])); Log.v(TAG, String.format("ctor | mNormalizedBacklightRange: [%f, %f]", normalizedBacklightRange[0], normalizedBacklightRange[normalizedBacklightRange.length - 1])); mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController()); mIsOverlayShowing = false; } Loading Loading @@ -331,13 +243,33 @@ public class UdfpsController implements DozeReceiver { } private WindowManager.LayoutParams computeLayoutParams() { // Default dimensions assume portrait mode. mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius; mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius; mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius; mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius; Point p = new Point(); // Gets the size based on the current rotation of the display. mContext.getDisplay().getRealSize(p); mCoreLayoutParams.width = p.x; mCoreLayoutParams.x = p.x; mCoreLayoutParams.height = p.y; mCoreLayoutParams.y = p.y; // Transform dimensions if the device is in landscape mode. switch (mContext.getDisplay().getRotation()) { case Surface.ROTATION_90: mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius; mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius; break; case Surface.ROTATION_270: mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius; mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius; break; default: // Do nothing to stay in portrait mode. } return mCoreLayoutParams; } Loading Loading @@ -402,36 +334,10 @@ public class UdfpsController implements DozeReceiver { }); } // Returns a value in the range of [0, 255]. private int computeScrimOpacity() { // Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f]. float backlightSetting = mSystemSettings.getFloatForUser( Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness, UserHandle.USER_CURRENT); // Constrain the backlight setting to [0.0f, 1.0f]. float backlightValue = MathUtils.constrain(backlightSetting, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); // Interpolate the backlight value to nits. float nits = mBacklightToNitsSpline.interpolate(backlightValue); // Interpolate nits to a backlight value for a panel with enabled HBM. float interpolatedHbmBacklightValue = mNitsToHbmBacklightSpline.interpolate(nits); float gammaCorrectedHbmBacklightValue = (float) Math.pow(interpolatedHbmBacklightValue, 1.0f / DISPLAY_GAMMA); float scrimOpacity = PowerManager.BRIGHTNESS_MAX - gammaCorrectedHbmBacklightValue; // Interpolate the opacity value from [0.0f, 1.0f] to [0, 255]. return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity); } /** * Request fingerprint scan. * * This is intented to be called in response to a sensor that triggers an AOD interrupt for the * This is intended to be called in response to a sensor that triggers an AOD interrupt for the * fingerprint sensor. */ void onAodInterrupt(int screenX, int screenY, float major, float minor) { Loading @@ -451,7 +357,7 @@ public class UdfpsController implements DozeReceiver { /** * Cancel fingerprint scan. * * This is intented to be called after the fingerprint scan triggered by the AOD interrupt * This is intended to be called after the fingerprint scan triggered by the AOD interrupt * either succeeds or fails. */ void onCancelAodInterrupt() { Loading @@ -466,65 +372,28 @@ public class UdfpsController implements DozeReceiver { onFingerUp(); } protected void onFingerDown(int x, int y, float minor, float major) { if (mHbmSupported) { try { FileWriter fw = new FileWriter(mHbmPath); fw.write(mHbmEnableCommand); fw.close(); } catch (IOException e) { mView.hideScrimAndDot(); Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage()); } } mView.setScrimAlpha(computeScrimOpacity()); mView.setRunAfterShowingScrimAndDot(() -> { mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); }); mView.showScrimAndDot(); // This method can be called from the UI thread. private void onFingerDown(int x, int y, float minor, float major) { mView.setOnIlluminatedRunnable( () -> mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major)); mView.startIllumination(); } protected void onFingerUp() { // This method can be called from the UI thread. private void onFingerUp() { mFingerprintManager.onPointerUp(mSensorProps.sensorId); // Hiding the scrim before disabling HBM results in less noticeable flicker. mView.hideScrimAndDot(); if (mHbmSupported) { try { FileWriter fw = new FileWriter(mHbmPath); fw.write(mHbmDisableCommand); fw.close(); } catch (IOException e) { mView.showScrimAndDot(); Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage()); } } mView.stopIllumination(); } private static float obtainDefaultBrightness(Context context) { return MathUtils.constrain(context.getDisplay().getBrightnessDefault(), PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); } private static float[] toFloatArray(TypedArray array) { final int n = array.length(); float[] vals = new float[n]; for (int i = 0; i < n; i++) { vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT); } array.recycle(); return vals; } private static float[] normalizeBacklightRange(int[] backlight) { final int n = backlight.length; float[] normalizedBacklight = new float[n]; for (int i = 0; i < n; i++) { normalizedBacklight[i] = BrightnessSynchronizer.brightnessIntToFloat(backlight[i]); } return normalizedBacklight; @Override public void enableHbm(Surface surface) { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } protected UdfpsView getView() { return mView; @Override public void disableHbm(Surface surface) { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } } packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +120 −143 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +16 −26 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import static org.mockito.Mockito.when; import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; Loading @@ -47,7 +46,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; Loading Loading @@ -86,15 +84,12 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private FingerprintManager mFingerprintManager; @Mock private DisplayManager mDisplayManager; @Mock private WindowManager mWindowManager; @Mock private StatusBarStateController mStatusBarStateController; @Mock private ScrimController mScrimController; private FakeSettings mSystemSettings; private FakeExecutor mFgExecutor; // Stuff for configuring mocks Loading @@ -109,7 +104,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor; private IUdfpsOverlayController mOverlayController; @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; @Captor private ArgumentCaptor<Runnable> mRunAfterShowingScrimAndDotCaptor; @Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor; @Before public void setUp() { Loading @@ -122,16 +117,13 @@ public class UdfpsControllerTest extends SysuiTestCase { FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, true /* resetLockoutRequiresHardwareAuthToken */)); when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); mSystemSettings = new FakeSettings(); mFgExecutor = new FakeExecutor(new FakeSystemClock()); mUdfpsController = new UdfpsController( mContext, mResources, mLayoutInflater, mFingerprintManager, mDisplayManager, mWindowManager, mSystemSettings, mStatusBarStateController, mFgExecutor, mScrimController); Loading Loading @@ -183,7 +175,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void fingerDown() throws RemoteException { // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isShowScrimAndDot()).thenReturn(false); when(mUdfpsView.isIlluminationRequested()).thenReturn(false); when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing Loading @@ -195,12 +187,11 @@ public class UdfpsControllerTest extends SysuiTestCase { MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); event.recycle(); // THEN the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); // AND a runnable that passes the event to FingerprintManager is set on the view verify(mUdfpsView).setRunAfterShowingScrimAndDot( mRunAfterShowingScrimAndDotCaptor.capture()); mRunAfterShowingScrimAndDotCaptor.getValue().run(); // THEN illumination begins verify(mUdfpsView).startIllumination(); // AND onIlluminatedRunnable that notifies FingerprintManager is set verify(mUdfpsView).setOnIlluminatedRunnable(mOnIlluminatedRunnableCaptor.capture()); mOnIlluminatedRunnableCaptor.getValue().run(); verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f)); } Loading @@ -213,12 +204,11 @@ public class UdfpsControllerTest extends SysuiTestCase { mFgExecutor.runAllReady(); // WHEN fingerprint is requested because of AOD interrupt mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); // THEN the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); // AND a runnable that passes the event to FingerprintManager is set on the view verify(mUdfpsView).setRunAfterShowingScrimAndDot( mRunAfterShowingScrimAndDotCaptor.capture()); mRunAfterShowingScrimAndDotCaptor.getValue().run(); // THEN illumination begins verify(mUdfpsView).startIllumination(); // AND onIlluminatedRunnable that notifies FingerprintManager is set verify(mUdfpsView).setOnIlluminatedRunnable(mOnIlluminatedRunnableCaptor.capture()); mOnIlluminatedRunnableCaptor.getValue().run(); verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */); } Loading @@ -232,8 +222,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it is cancelled mUdfpsController.onCancelAodInterrupt(); // THEN the scrim and dot is hidden verify(mUdfpsView).hideScrimAndDot(); // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } @Test Loading @@ -246,8 +236,8 @@ public class UdfpsControllerTest extends SysuiTestCase { // WHEN it times out mFgExecutor.advanceClockToNext(); mFgExecutor.runAllReady(); // THEN the scrim and dot is hidden verify(mUdfpsView).hideScrimAndDot(); // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } @Test Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +48 −179 Original line number Diff line number Diff line Loading @@ -22,29 +22,22 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RectF; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.MathUtils; import android.util.Spline; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; Loading @@ -53,10 +46,6 @@ import com.android.systemui.doze.DozeReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.settings.SystemSettings; import java.io.FileWriter; import java.io.IOException; import javax.inject.Inject; Loading @@ -73,16 +62,13 @@ import javax.inject.Inject; */ @SuppressWarnings("deprecation") @SysUISingleton public class UdfpsController implements DozeReceiver { public class UdfpsController implements UdfpsView.HbmCallback, DozeReceiver { private static final String TAG = "UdfpsController"; // Gamma approximation for the sRGB color space. private static final float DISPLAY_GAMMA = 2.2f; private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; private final Context mContext; private final FingerprintManager mFingerprintManager; private final WindowManager mWindowManager; private final SystemSettings mSystemSettings; private final DelayableExecutor mFgExecutor; private final StatusBarStateController mStatusBarStateController; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple Loading @@ -90,23 +76,6 @@ public class UdfpsController implements DozeReceiver { @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; private final WindowManager.LayoutParams mCoreLayoutParams; private final UdfpsView mView; // Debugfs path to control the high-brightness mode. private final String mHbmPath; private final String mHbmEnableCommand; private final String mHbmDisableCommand; private final boolean mHbmSupported; // Brightness in nits in the high-brightness mode. private final float mMaxNits; // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to a // brightness in nits. private final Spline mBacklightToNitsSpline; // A spline mapping from a value in nits to a backlight value of a hypothetical panel whose // maximum backlight value corresponds to our panel's high-brightness mode. // The output is normalized to the range [0, 1.0]. private Spline mNitsToHbmBacklightSpline; // Default non-HBM backlight value normalized to the range [0, 1.0]. Used as a fallback when the // actual brightness value cannot be retrieved. private final float mDefaultBrightness; // Indicates whether the overlay is currently showing. Even if it has been requested, it might // not be showing. private boolean mIsOverlayShowing; Loading Loading @@ -152,7 +121,7 @@ public class UdfpsController implements DozeReceiver { @SuppressLint("ClickableViewAccessibility") private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> { UdfpsView view = (UdfpsView) v; final boolean isFingerDown = view.isShowScrimAndDot(); final boolean isFingerDown = view.isIlluminationRequested(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: Loading Loading @@ -183,9 +152,7 @@ public class UdfpsController implements DozeReceiver { @Main Resources resources, LayoutInflater inflater, @Nullable FingerprintManager fingerprintManager, DisplayManager displayManager, WindowManager windowManager, SystemSettings systemSettings, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, @NonNull ScrimController scrimController) { Loading @@ -194,7 +161,6 @@ public class UdfpsController implements DozeReceiver { // fingerprint manager should never be null. mFingerprintManager = checkNotNull(fingerprintManager); mWindowManager = windowManager; mSystemSettings = systemSettings; mFgExecutor = fgExecutor; mStatusBarStateController = statusBarStateController; Loading @@ -211,72 +177,18 @@ public class UdfpsController implements DozeReceiver { PixelFormat.TRANSLUCENT); mCoreLayoutParams.setTitle(TAG); mCoreLayoutParams.setFitInsetsTypes(0); mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mCoreLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false); mView.setSensorProperties(mSensorProps); mView.setHbmCallback(this); mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path); mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command); mHbmDisableCommand = resources.getString(R.string.udfps_hbm_disable_command); mHbmSupported = !TextUtils.isEmpty(mHbmPath); mView.setHbmSupported(mHbmSupported); scrimController.addScrimChangedListener(mView); statusBarStateController.addCallback(mView); // This range only consists of the minimum and maximum values, which only cover // non-high-brightness mode. float[] nitsRange = toFloatArray(resources.obtainTypedArray( com.android.internal.R.array.config_screenBrightnessNits)); if (nitsRange.length < 2) { throw new IllegalArgumentException( String.format("nitsRange.length: %d. Must be >= 2", nitsRange.length)); } // The last value of this range corresponds to the high-brightness mode. float[] nitsAutoBrightnessValues = toFloatArray(resources.obtainTypedArray( com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); if (nitsAutoBrightnessValues.length < 2) { throw new IllegalArgumentException( String.format("nitsAutoBrightnessValues.length: %d. Must be >= 2", nitsAutoBrightnessValues.length)); } mMaxNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1]; float[] hbmNitsRange = nitsRange.clone(); hbmNitsRange[hbmNitsRange.length - 1] = mMaxNits; // This range only consists of the minimum and maximum backlight values, which only apply // in non-high-brightness mode. float[] normalizedBacklightRange = normalizeBacklightRange( resources.getIntArray( com.android.internal.R.array.config_screenBrightnessBacklight)); if (normalizedBacklightRange.length < 2) { throw new IllegalArgumentException( String.format("normalizedBacklightRange.length: %d. Must be >= 2", normalizedBacklightRange.length)); } if (normalizedBacklightRange.length != nitsRange.length) { throw new IllegalArgumentException( "normalizedBacklightRange.length != nitsRange.length"); } mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange); mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange); mDefaultBrightness = obtainDefaultBrightness(mContext); // TODO(b/160025856): move to the "dump" method. Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0], nitsRange[nitsRange.length - 1])); Log.v(TAG, String.format("ctor | mHbmNitsRange: [%f, %f]", hbmNitsRange[0], hbmNitsRange[hbmNitsRange.length - 1])); Log.v(TAG, String.format("ctor | mNormalizedBacklightRange: [%f, %f]", normalizedBacklightRange[0], normalizedBacklightRange[normalizedBacklightRange.length - 1])); mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController()); mIsOverlayShowing = false; } Loading Loading @@ -331,13 +243,33 @@ public class UdfpsController implements DozeReceiver { } private WindowManager.LayoutParams computeLayoutParams() { // Default dimensions assume portrait mode. mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius; mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius; mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius; mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius; Point p = new Point(); // Gets the size based on the current rotation of the display. mContext.getDisplay().getRealSize(p); mCoreLayoutParams.width = p.x; mCoreLayoutParams.x = p.x; mCoreLayoutParams.height = p.y; mCoreLayoutParams.y = p.y; // Transform dimensions if the device is in landscape mode. switch (mContext.getDisplay().getRotation()) { case Surface.ROTATION_90: mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius; mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius; break; case Surface.ROTATION_270: mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius; mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius; break; default: // Do nothing to stay in portrait mode. } return mCoreLayoutParams; } Loading Loading @@ -402,36 +334,10 @@ public class UdfpsController implements DozeReceiver { }); } // Returns a value in the range of [0, 255]. private int computeScrimOpacity() { // Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f]. float backlightSetting = mSystemSettings.getFloatForUser( Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness, UserHandle.USER_CURRENT); // Constrain the backlight setting to [0.0f, 1.0f]. float backlightValue = MathUtils.constrain(backlightSetting, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); // Interpolate the backlight value to nits. float nits = mBacklightToNitsSpline.interpolate(backlightValue); // Interpolate nits to a backlight value for a panel with enabled HBM. float interpolatedHbmBacklightValue = mNitsToHbmBacklightSpline.interpolate(nits); float gammaCorrectedHbmBacklightValue = (float) Math.pow(interpolatedHbmBacklightValue, 1.0f / DISPLAY_GAMMA); float scrimOpacity = PowerManager.BRIGHTNESS_MAX - gammaCorrectedHbmBacklightValue; // Interpolate the opacity value from [0.0f, 1.0f] to [0, 255]. return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity); } /** * Request fingerprint scan. * * This is intented to be called in response to a sensor that triggers an AOD interrupt for the * This is intended to be called in response to a sensor that triggers an AOD interrupt for the * fingerprint sensor. */ void onAodInterrupt(int screenX, int screenY, float major, float minor) { Loading @@ -451,7 +357,7 @@ public class UdfpsController implements DozeReceiver { /** * Cancel fingerprint scan. * * This is intented to be called after the fingerprint scan triggered by the AOD interrupt * This is intended to be called after the fingerprint scan triggered by the AOD interrupt * either succeeds or fails. */ void onCancelAodInterrupt() { Loading @@ -466,65 +372,28 @@ public class UdfpsController implements DozeReceiver { onFingerUp(); } protected void onFingerDown(int x, int y, float minor, float major) { if (mHbmSupported) { try { FileWriter fw = new FileWriter(mHbmPath); fw.write(mHbmEnableCommand); fw.close(); } catch (IOException e) { mView.hideScrimAndDot(); Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage()); } } mView.setScrimAlpha(computeScrimOpacity()); mView.setRunAfterShowingScrimAndDot(() -> { mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); }); mView.showScrimAndDot(); // This method can be called from the UI thread. private void onFingerDown(int x, int y, float minor, float major) { mView.setOnIlluminatedRunnable( () -> mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major)); mView.startIllumination(); } protected void onFingerUp() { // This method can be called from the UI thread. private void onFingerUp() { mFingerprintManager.onPointerUp(mSensorProps.sensorId); // Hiding the scrim before disabling HBM results in less noticeable flicker. mView.hideScrimAndDot(); if (mHbmSupported) { try { FileWriter fw = new FileWriter(mHbmPath); fw.write(mHbmDisableCommand); fw.close(); } catch (IOException e) { mView.showScrimAndDot(); Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage()); } } mView.stopIllumination(); } private static float obtainDefaultBrightness(Context context) { return MathUtils.constrain(context.getDisplay().getBrightnessDefault(), PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); } private static float[] toFloatArray(TypedArray array) { final int n = array.length(); float[] vals = new float[n]; for (int i = 0; i < n; i++) { vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT); } array.recycle(); return vals; } private static float[] normalizeBacklightRange(int[] backlight) { final int n = backlight.length; float[] normalizedBacklight = new float[n]; for (int i = 0; i < n; i++) { normalizedBacklight[i] = BrightnessSynchronizer.brightnessIntToFloat(backlight[i]); } return normalizedBacklight; @Override public void enableHbm(Surface surface) { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } protected UdfpsView getView() { return mView; @Override public void disableHbm(Surface surface) { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } }
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +120 −143 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +16 −26 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import static org.mockito.Mockito.when; import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; Loading @@ -47,7 +46,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; Loading Loading @@ -86,15 +84,12 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private FingerprintManager mFingerprintManager; @Mock private DisplayManager mDisplayManager; @Mock private WindowManager mWindowManager; @Mock private StatusBarStateController mStatusBarStateController; @Mock private ScrimController mScrimController; private FakeSettings mSystemSettings; private FakeExecutor mFgExecutor; // Stuff for configuring mocks Loading @@ -109,7 +104,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor; private IUdfpsOverlayController mOverlayController; @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; @Captor private ArgumentCaptor<Runnable> mRunAfterShowingScrimAndDotCaptor; @Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor; @Before public void setUp() { Loading @@ -122,16 +117,13 @@ public class UdfpsControllerTest extends SysuiTestCase { FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, true /* resetLockoutRequiresHardwareAuthToken */)); when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); mSystemSettings = new FakeSettings(); mFgExecutor = new FakeExecutor(new FakeSystemClock()); mUdfpsController = new UdfpsController( mContext, mResources, mLayoutInflater, mFingerprintManager, mDisplayManager, mWindowManager, mSystemSettings, mStatusBarStateController, mFgExecutor, mScrimController); Loading Loading @@ -183,7 +175,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void fingerDown() throws RemoteException { // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isShowScrimAndDot()).thenReturn(false); when(mUdfpsView.isIlluminationRequested()).thenReturn(false); when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing Loading @@ -195,12 +187,11 @@ public class UdfpsControllerTest extends SysuiTestCase { MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); event.recycle(); // THEN the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); // AND a runnable that passes the event to FingerprintManager is set on the view verify(mUdfpsView).setRunAfterShowingScrimAndDot( mRunAfterShowingScrimAndDotCaptor.capture()); mRunAfterShowingScrimAndDotCaptor.getValue().run(); // THEN illumination begins verify(mUdfpsView).startIllumination(); // AND onIlluminatedRunnable that notifies FingerprintManager is set verify(mUdfpsView).setOnIlluminatedRunnable(mOnIlluminatedRunnableCaptor.capture()); mOnIlluminatedRunnableCaptor.getValue().run(); verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f)); } Loading @@ -213,12 +204,11 @@ public class UdfpsControllerTest extends SysuiTestCase { mFgExecutor.runAllReady(); // WHEN fingerprint is requested because of AOD interrupt mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); // THEN the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); // AND a runnable that passes the event to FingerprintManager is set on the view verify(mUdfpsView).setRunAfterShowingScrimAndDot( mRunAfterShowingScrimAndDotCaptor.capture()); mRunAfterShowingScrimAndDotCaptor.getValue().run(); // THEN illumination begins verify(mUdfpsView).startIllumination(); // AND onIlluminatedRunnable that notifies FingerprintManager is set verify(mUdfpsView).setOnIlluminatedRunnable(mOnIlluminatedRunnableCaptor.capture()); mOnIlluminatedRunnableCaptor.getValue().run(); verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */); } Loading @@ -232,8 +222,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it is cancelled mUdfpsController.onCancelAodInterrupt(); // THEN the scrim and dot is hidden verify(mUdfpsView).hideScrimAndDot(); // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } @Test Loading @@ -246,8 +236,8 @@ public class UdfpsControllerTest extends SysuiTestCase { // WHEN it times out mFgExecutor.advanceClockToNext(); mFgExecutor.runAllReady(); // THEN the scrim and dot is hidden verify(mUdfpsView).hideScrimAndDot(); // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } @Test Loading