Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f0a677a authored by Shawn Lin's avatar Shawn Lin
Browse files

Support face auth animation for foldable devices

- Add another face sensor location config for the inner display.
- Load the location configs according to the current available camera
  id:
  - Add the face sensor field to FacePropertyRepository and listen to
    the CameraManager.AvailabilityCallback to monitor the current
    available camera id.
  - Remove all the face sensor location relative logic in AuthController
    and move it to FacePropertyRepository.
  - Everything that wants to get the face sensor location is now getting
    it from FacePropertyRepository instead of AuthController.

Bug: 311564599
Test: atest DisplayStateRepositoryTest FacePropertyRepositoryImplTest
            ScreenDecorationsTest FaceScanningProviderFactoryTest
	    KeyguardRepositoryImplTest AuthRippleControllerTest
Flag: NONE
Change-Id: Ic6a7ba7e475dad212ac4c5a4cc23f65cf7815a1e
parent b63e9281
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.common.shared.model.Position
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.DozeMachine
@@ -71,6 +72,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private lateinit var systemClock: FakeSystemClock
    private lateinit var facePropertyRepository: FakeFacePropertyRepository

    private lateinit var underTest: KeyguardRepositoryImpl

@@ -78,6 +80,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        systemClock = FakeSystemClock()
        facePropertyRepository = FakeFacePropertyRepository()
        underTest =
            KeyguardRepositoryImpl(
                statusBarStateController,
@@ -89,6 +92,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                mainDispatcher,
                testScope.backgroundScope,
                systemClock,
                facePropertyRepository,
            )
    }

@@ -482,10 +486,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
        testScope.runTest {
            val values = mutableListOf<Point?>()
            val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)

            val captor = argumentCaptor<AuthController.Callback>()
            runCurrent()
            verify(authController).addCallback(captor.capture())

            // An initial, null value should be initially emitted so that flows combined with this
            // one
@@ -500,8 +501,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                    Point(250, 250),
                )
                .onEach {
                    whenever(authController.faceSensorLocation).thenReturn(it)
                    captor.value.onFaceSensorLocationChanged()
                    facePropertyRepository.setSensorLocation(it)
                    runCurrent()
                }
                .also { dispatchedSensorLocations ->
+11 −4
Original line number Diff line number Diff line
@@ -546,7 +546,7 @@
    <!-- Similar to config_frontBuiltInDisplayCutoutProtection but for inner display. -->
    <string translatable="false" name="config_innerBuiltInDisplayCutoutProtection"></string>

    <!-- ID for the camera of inner display that needs extra protection -->
    <!-- ID for the camera of inner display that needs extra protection. -->
    <string translatable="false" name="config_protectedInnerCameraId"></string>
    <!-- Physical ID for the camera of inner display that needs extra protection -->
    <string translatable="false" name="config_protectedInnerPhysicalCameraId"></string>
@@ -650,13 +650,20 @@
    <!-- Whether to use window background blur for the volume dialog. -->
    <bool name="config_volumeDialogUseBackgroundBlur">false</bool>

    <!-- The properties of the face auth camera in pixels -->
    <!-- The properties of the face auth front camera for outer display in pixels -->
    <integer-array name="config_face_auth_props">
        <!-- sensorLocationX -->
        <!-- sensorLocationY -->
        <!--sensorRadius -->
    </integer-array>

    <!-- The properties of the face auth front camera for inner display in pixels -->
    <integer-array name="config_inner_face_auth_props">
        <!-- sensorLocationX -->
        <!-- sensorLocationY -->
        <!--sensorRadius -->
    </integer-array>

    <!-- Overrides the behavior of the face unlock keyguard bypass setting:
         0 - Don't override the setting (default)
         1 - Override the setting to always bypass keyguard
+21 −19
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ import androidx.annotation.VisibleForTesting;

import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.data.repository.FacePropertyRepository;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.DebugRoundedCornerDelegate;
@@ -92,6 +92,7 @@ import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;

import dalvik.annotation.optimization.NeverCompile;
@@ -131,8 +132,6 @@ public class ScreenDecorations implements
    };
    private final ScreenDecorationsLogger mLogger;

    private final AuthController mAuthController;

    private DisplayTracker mDisplayTracker;
    @VisibleForTesting
    protected boolean mIsRegistered;
@@ -183,6 +182,9 @@ public class ScreenDecorations implements
    private DisplayCutout mDisplayCutout;
    private boolean mPendingManualConfigUpdate;

    private FacePropertyRepository mFacePropertyRepository;
    private JavaAdapter mJavaAdapter;

    @VisibleForTesting
    protected void showCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
        if (mFaceScanningFactory.shouldShowFaceScanningAnim()) {
@@ -330,7 +332,8 @@ public class ScreenDecorations implements
            PrivacyDotDecorProviderFactory dotFactory,
            FaceScanningProviderFactory faceScanningFactory,
            ScreenDecorationsLogger logger,
            AuthController authController) {
            FacePropertyRepository facePropertyRepository,
            JavaAdapter javaAdapter) {
        mContext = context;
        mSecureSettings = secureSettings;
        mCommandRegistry = commandRegistry;
@@ -342,21 +345,9 @@ public class ScreenDecorations implements
        mFaceScanningFactory = faceScanningFactory;
        mFaceScanningViewId = com.android.systemui.res.R.id.face_scanning_anim;
        mLogger = logger;
        mAuthController = authController;
    }


    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
        @Override
        public void onFaceSensorLocationChanged() {
            mLogger.onSensorLocationChanged();
            if (mExecutor != null) {
                mExecutor.execute(
                        () -> updateOverlayProviderViews(
                                new Integer[]{mFaceScanningViewId}));
        mFacePropertyRepository = facePropertyRepository;
        mJavaAdapter = javaAdapter;
    }
        }
    };

    private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
        // If we are exiting debug mode, we can set it (false) and bail, otherwise we will
@@ -407,7 +398,8 @@ public class ScreenDecorations implements
        mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
        mExecutor.execute(this::startOnScreenDecorationsThread);
        mDotViewController.setUiExecutor(mExecutor);
        mAuthController.addCallback(mAuthControllerCallback);
        mJavaAdapter.alwaysCollectFlow(mFacePropertyRepository.getSensorLocation(),
                this::onFaceSensorLocationChanged);
        mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
                () -> new ScreenDecorCommand(mScreenDecorCommandCallback));
    }
@@ -1320,6 +1312,16 @@ public class ScreenDecorations implements
        view.setLayoutParams(params);
    }

    @VisibleForTesting
    void onFaceSensorLocationChanged(Point location) {
        mLogger.onSensorLocationChanged();
        if (mExecutor != null) {
            mExecutor.execute(
                    () -> updateOverlayProviderViews(
                            new Integer[]{mFaceScanningViewId}));
        }
    }

    public static class DisplayCutoutView extends DisplayCutoutBaseView {
        final List<Rect> mBounds = new ArrayList();
        final Rect mBoundingRect = new Rect();
+0 −48
Original line number Diff line number Diff line
@@ -148,10 +148,6 @@ public class AuthController implements

    private final Display mDisplay;
    private float mScaleFactor = 1f;
    // sensor locations without any resolution scaling nor rotation adjustments:
    @Nullable private final Point mFaceSensorLocationDefault;
    // cached sensor locations:
    @Nullable private Point mFaceSensorLocation;
    @Nullable private Point mFingerprintSensorLocation;
    @Nullable private Rect mUdfpsBounds;
    private final Set<Callback> mCallbacks = new HashSet<>();
@@ -622,7 +618,6 @@ public class AuthController implements
        mScaleFactor = mUdfpsUtils.getScaleFactor(mCachedDisplayInfo);
        updateUdfpsLocation();
        updateFingerprintLocation();
        updateFaceLocation();
    }
    /**
     * @return where the fingerprint sensor exists in pixels in its natural orientation.
@@ -681,31 +676,6 @@ public class AuthController implements
        return mFpProps;
    }

    /**
     * @return where the face sensor exists in pixels in the current device orientation. Returns
     * null if no face sensor exists.
     */
    @Nullable public Point getFaceSensorLocation() {
        return mFaceSensorLocation;
    }

    private void updateFaceLocation() {
        if (mFaceProps == null || mFaceSensorLocationDefault == null) {
            mFaceSensorLocation = null;
        } else {
            mFaceSensorLocation = rotateToCurrentOrientation(
                    new Point(
                            (int) (mFaceSensorLocationDefault.x * mScaleFactor),
                            (int) (mFaceSensorLocationDefault.y * mScaleFactor)),
                    mCachedDisplayInfo
            );
        }

        for (final Callback cb : mCallbacks) {
            cb.onFaceSensorLocationChanged();
        }
    }

    /**
     * @param inOutPoint point on the display in pixels. Going in, represents the point
     *                   in the device's natural orientation. Going out, represents
@@ -821,17 +791,7 @@ public class AuthController implements
        mWakefulnessLifecycle = wakefulnessLifecycle;
        mPanelInteractionDetector = panelInteractionDetector;


        mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
        int[] faceAuthLocation = context.getResources().getIntArray(
                com.android.systemui.res.R.array.config_face_auth_props);
        if (faceAuthLocation == null || faceAuthLocation.length < 2) {
            mFaceSensorLocationDefault = null;
        } else {
            mFaceSensorLocationDefault = new Point(
                    faceAuthLocation[0],
                    faceAuthLocation[1]);
        }

        mDisplay = mContext.getDisplay();
        updateSensorLocations();
@@ -1358,8 +1318,6 @@ public class AuthController implements
        final AuthDialog dialog = mCurrentDialog;
        pw.println("  mCachedDisplayInfo=" + mCachedDisplayInfo);
        pw.println("  mScaleFactor=" + mScaleFactor);
        pw.println("  faceAuthSensorLocationDefault=" + mFaceSensorLocationDefault);
        pw.println("  faceAuthSensorLocation=" + getFaceSensorLocation());
        pw.println("  fingerprintSensorLocationInNaturalOrientation="
                + getFingerprintSensorLocationInNaturalOrientation());
        pw.println("  fingerprintSensorLocation=" + getFingerprintSensorLocation());
@@ -1433,11 +1391,5 @@ public class AuthController implements
         * {@link #onFingerprintLocationChanged}.
         */
        default void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {}

        /**
         * Called when the location of the face unlock sensor (typically the front facing camera)
         * changes. The location in pixels can change due to resolution changes.
         */
        default void onFaceSensorLocationChanged() {}
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
@@ -80,6 +81,7 @@ class AuthRippleController @Inject constructor(
    private val logger: KeyguardLogger,
    private val biometricUnlockController: BiometricUnlockController,
    private val lightRevealScrim: LightRevealScrim,
    private val facePropertyRepository: FacePropertyRepository,
    rippleView: AuthRippleView?
) :
    ViewController<AuthRippleView>(rippleView),
@@ -263,7 +265,7 @@ class AuthRippleController @Inject constructor(

    fun updateSensorLocation() {
        fingerprintSensorLocation = authController.fingerprintSensorLocation
        faceSensorLocation = authController.faceSensorLocation
        faceSensorLocation = facePropertyRepository.sensorLocation.value
    }

    private fun updateRippleColor() {
Loading