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

Commit 4c4961ab authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Hide Udfps Overlay together with system transitions

Prevent the UDFPS overlay from showing when system dialogs are
expected to be closed. Since the UDFPS affordances are not
actually shown in the application's window, we should nicely
hide the UDFPS overlay together with the system transitions.

1) Adds IUdfpsOverlayControllerCallback for SysUI to notify
   FingerprintService of per-session events.
2) Adds AcquisitionClient#onUserCanceled, which notifies the
   caller of ERROR_USER_CANCELED, and then cancels the HAL
   request

Bug: 178403701
Test: Enroll, swipe up. UDFPS affordances animate away together
      with the activity. BiometricScheduler logs also indicate
      enrollment canceled.
Test: atest AcquisitionClientTest
Test: atest UdfpsControllerTest

Change-Id: I0b2c9eb20ef0686e426ea12896769e4df077cadb
parent 58fd4557
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package android.hardware.fingerprint;

import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;

/**
 * Interface for interacting with the under-display fingerprint sensor (UDFPS) overlay.
 * @hide
@@ -28,7 +30,7 @@ oneway interface IUdfpsOverlayController {
    const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage

    // Shows the overlay.
    void showUdfpsOverlay(int sensorId, int reason);
    void showUdfpsOverlay(int sensorId, int reason, IUdfpsOverlayControllerCallback callback);

    // Hides the overlay.
    void hideUdfpsOverlay(int sensorId);
+24 −0
Original line number Diff line number Diff line
/*
 * 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 android.hardware.fingerprint;

/**
 * @hide
 */
oneway interface IUdfpsOverlayControllerCallback {
    // Notify system_server if the user cancels a UDFPS-related operation (enroll, auth)
    void onUserCanceled();
}
+37 −3
Original line number Diff line number Diff line
@@ -20,7 +20,10 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -29,6 +32,8 @@ import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.SystemClock;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -110,10 +115,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
    private static class ServerRequest {
        // Reason the overlay has been requested. See IUdfpsOverlayController for definitions.
        final int mRequestReason;
        @NonNull final IUdfpsOverlayControllerCallback mCallback;
        @Nullable final UdfpsEnrollHelper mEnrollHelper;

        ServerRequest(int requestReason, @Nullable UdfpsEnrollHelper enrollHelper) {
        ServerRequest(int requestReason, @NonNull IUdfpsOverlayControllerCallback callback,
                @Nullable UdfpsEnrollHelper enrollHelper) {
            mRequestReason = requestReason;
            mCallback = callback;
            mEnrollHelper = enrollHelper;
        }

@@ -128,11 +136,20 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                mEnrollHelper.onEnrollmentHelp();
            }
        }

        void onUserCanceled() {
            try {
                mCallback.onUserCanceled();
            } catch (RemoteException e) {
                Log.e(TAG, "Remote exception", e);
            }
        }
    }

    public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
        @Override
        public void showUdfpsOverlay(int sensorId, int reason) {
        public void showUdfpsOverlay(int sensorId, int reason,
                @NonNull IUdfpsOverlayControllerCallback callback) {
            final UdfpsEnrollHelper enrollHelper;
            if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
                    || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
@@ -141,7 +158,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                enrollHelper = null;
            }

            mServerRequest = new ServerRequest(reason, enrollHelper);
            mServerRequest = new ServerRequest(reason, callback, enrollHelper);
            updateOverlay();
        }

@@ -195,6 +212,19 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
        return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
    }

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (mServerRequest != null
                    && Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
                Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received");
                mServerRequest.onUserCanceled();
                mServerRequest = null;
                updateOverlay();
            }
        }
    };

    @SuppressLint("ClickableViewAccessibility")
    private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) -> {
        UdfpsView udfpsView = (UdfpsView) view;
@@ -310,6 +340,10 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
        mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;

        mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());

        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        context.registerReceiver(mBroadcastReceiver, filter);
    }

    @Nullable
+11 −8
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.PowerManager;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -89,6 +90,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
    private StatusBarStateController mStatusBarStateController;
    @Mock
    private StatusBar mStatusBar;
    @Mock
    private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;

    private FakeExecutor mFgExecutor;

@@ -152,7 +155,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    @Test
    public void dozeTimeTick() throws RemoteException {
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        mUdfpsController.dozeTimeTick();
        verify(mUdfpsView).dozeTimeTick();
@@ -161,7 +164,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    @Test
    public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        verify(mWindowManager).addView(eq(mUdfpsView), any());
    }
@@ -169,7 +172,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    @Test
    public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
        mFgExecutor.runAllReady();
        verify(mWindowManager).removeView(eq(mUdfpsView));
@@ -183,7 +186,7 @@ public class UdfpsControllerTest extends SysuiTestCase {

        // GIVEN that the overlay is showing
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        // WHEN ACTION_DOWN is received
        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -205,7 +208,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    public void aodInterrupt() throws RemoteException {
        // GIVEN that the overlay is showing
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        // WHEN fingerprint is requested because of AOD interrupt
        mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
@@ -221,7 +224,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    public void cancelAodInterrupt() throws RemoteException {
        // GIVEN AOD interrupt
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
        // WHEN it is cancelled
@@ -234,7 +237,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    public void aodInterruptTimeout() throws RemoteException {
        // GIVEN AOD interrupt
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
        // WHEN it times out
@@ -247,7 +250,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    @Test
    public void registersAndUnregistersViewForCallbacks() throws RemoteException {
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
        mFgExecutor.runAllReady();
        verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
        verify(mStatusBar).addExpansionChangedListener(
+12 −0
Original line number Diff line number Diff line
@@ -80,6 +80,18 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
        onErrorInternal(errorCode, vendorCode, true /* finish */);
    }

    /**
     * Notifies the caller that the operation was canceled by the user. Note that the actual
     * operation still needs to wait for the HAL to send ERROR_CANCELED.
     */
    public void onUserCanceled() {
        // Send USER_CANCELED, but do not finish. Wait for the HAL to respond with ERROR_CANCELED,
        // which then finishes the AcquisitionClient's lifecycle.
        onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, 0 /* vendorCode */,
                false /* finish */);
        stopHalOperation();
    }

    protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) {
        // In some cases, the framework will send an error to the caller before a true terminal
        // case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
Loading