Merge "Fix mismatched state between SystemUI and FingerprintService" into sc-v2-dev am: 255aae7392

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16460257

Change-Id: I3b25cc962374df059ac5c52e0aa654602cd4c3c4
This commit is contained in:
TreeHugger Robot
2021-12-21 07:36:36 +00:00
committed by Automerger Merge Worker
2 changed files with 159 additions and 66 deletions

View File

@@ -49,7 +49,6 @@ import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.hardware.fingerprint.IUdfpsHbmListener; import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
@@ -65,6 +64,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver; import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -92,15 +92,20 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
private static final boolean DEBUG = true; private static final boolean DEBUG = true;
private static final int SENSOR_PRIVACY_DELAY = 500; private static final int SENSOR_PRIVACY_DELAY = 500;
private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Handler mHandler;
private final Execution mExecution;
private final CommandQueue mCommandQueue; private final CommandQueue mCommandQueue;
private final ActivityTaskManager mActivityTaskManager; private final ActivityTaskManager mActivityTaskManager;
@Nullable private final FingerprintManager mFingerprintManager; @Nullable
@Nullable private final FaceManager mFaceManager; private final FingerprintManager mFingerprintManager;
@Nullable
private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory; private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory; private final Provider<SidefpsController> mSidefpsControllerFactory;
@Nullable private final PointF mFaceAuthSensorLocation; @Nullable
@Nullable private PointF mFingerprintLocation; private final PointF mFaceAuthSensorLocation;
@Nullable
private PointF mFingerprintLocation;
private final Set<Callback> mCallbacks = new HashSet<>(); private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState // TODO: These should just be saved from onSaveState
@@ -133,62 +138,27 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
} }
} }
private final IFingerprintAuthenticatorsRegisteredCallback
mFingerprintAuthenticatorsRegisteredCallback =
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@Override
public void onAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> sensors) {
mHandler.post(() -> handleAllAuthenticatorsRegistered(sensors));
}
};
private final FingerprintStateListener mFingerprintStateListener = private final FingerprintStateListener mFingerprintStateListener =
new FingerprintStateListener() { new FingerprintStateListener() {
@Override @Override
public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
Log.d(TAG, "onEnrollmentsChanged, userId: " + userId mHandler.post(
+ ", sensorId: " + sensorId () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
+ ", hasEnrollments: " + hasEnrollments);
for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
if (prop.sensorId == sensorId) {
mUdfpsEnrolledForUser.put(userId, hasEnrollments);
}
}
for (Callback cb : mCallbacks) {
cb.onEnrollmentsChanged();
}
} }
}; };
@NonNull @VisibleForTesting
private final IFingerprintAuthenticatorsRegisteredCallback final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
mFingerprintAuthenticatorsRegisteredCallback =
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@Override public void onAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> sensors) {
if (DEBUG) {
Log.d(TAG, "onFingerprintProvidersAvailable | sensors: " + Arrays.toString(
sensors.toArray()));
}
mFpProps = sensors;
List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
for (FingerprintSensorPropertiesInternal props : mFpProps) {
if (props.isAnyUdfpsType()) {
udfpsProps.add(props);
}
if (props.isAnySidefpsType()) {
sidefpsProps.add(props);
}
}
mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
if (mUdfpsProps != null) {
mUdfpsController = mUdfpsControllerFactory.get();
}
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) {
mSidefpsController = mSidefpsControllerFactory.get();
}
for (Callback cb : mCallbacks) {
cb.onAllAuthenticatorsRegistered();
}
}
};
@VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (mCurrentDialog != null if (mCurrentDialog != null
@@ -212,6 +182,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}; };
private void handleTaskStackChanged() { private void handleTaskStackChanged() {
mExecution.assertIsMainThread();
if (mCurrentDialog != null) { if (mCurrentDialog != null) {
try { try {
final String clientPackage = mCurrentDialog.getOpPackageName(); final String clientPackage = mCurrentDialog.getOpPackageName();
@@ -241,6 +212,56 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
} }
} }
private void handleAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> sensors) {
mExecution.assertIsMainThread();
if (DEBUG) {
Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString(
sensors.toArray()));
}
mFpProps = sensors;
List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
for (FingerprintSensorPropertiesInternal props : mFpProps) {
if (props.isAnyUdfpsType()) {
udfpsProps.add(props);
}
if (props.isAnySidefpsType()) {
sidefpsProps.add(props);
}
}
mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
if (mUdfpsProps != null) {
mUdfpsController = mUdfpsControllerFactory.get();
}
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) {
mSidefpsController = mSidefpsControllerFactory.get();
}
for (Callback cb : mCallbacks) {
cb.onAllAuthenticatorsRegistered();
}
mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
}
private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
mExecution.assertIsMainThread();
Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
+ ", hasEnrollments: " + hasEnrollments);
if (mUdfpsProps == null) {
Log.d(TAG, "handleEnrollmentsChanged, mUdfpsProps is null");
} else {
for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
if (prop.sensorId == sensorId) {
mUdfpsEnrolledForUser.put(userId, hasEnrollments);
}
}
}
for (Callback cb : mCallbacks) {
cb.onEnrollmentsChanged();
}
}
/** /**
* Adds a callback. See {@link Callback}. * Adds a callback. See {@link Callback}.
*/ */
@@ -449,6 +470,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@Inject @Inject
public AuthController(Context context, public AuthController(Context context,
Execution execution,
CommandQueue commandQueue, CommandQueue commandQueue,
ActivityTaskManager activityTaskManager, ActivityTaskManager activityTaskManager,
@NonNull WindowManager windowManager, @NonNull WindowManager windowManager,
@@ -459,6 +481,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@NonNull DisplayManager displayManager, @NonNull DisplayManager displayManager,
@Main Handler handler) { @Main Handler handler) {
super(context); super(context);
mExecution = execution;
mHandler = handler;
mCommandQueue = commandQueue; mCommandQueue = commandQueue;
mActivityTaskManager = activityTaskManager; mActivityTaskManager = activityTaskManager;
mFingerprintManager = fingerprintManager; mFingerprintManager = fingerprintManager;
@@ -470,7 +494,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mOrientationListener = new BiometricDisplayListener( mOrientationListener = new BiometricDisplayListener(
context, context,
displayManager, displayManager,
handler, mHandler,
BiometricDisplayListener.SensorType.Generic.INSTANCE, BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> { () -> {
onOrientationChanged(); onOrientationChanged();
@@ -521,7 +545,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
if (mFingerprintManager != null) { if (mFingerprintManager != null) {
mFingerprintManager.addAuthenticatorsRegisteredCallback( mFingerprintManager.addAuthenticatorsRegisteredCallback(
mFingerprintAuthenticatorsRegisteredCallback); mFingerprintAuthenticatorsRegisteredCallback);
mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
} }
mTaskStackListener = new BiometricTaskStackListener(); mTaskStackListener = new BiometricTaskStackListener();

View File

@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -53,12 +54,14 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableContext; import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableLooper.RunWithLooper;
import android.view.WindowManager; import android.view.WindowManager;
@@ -67,6 +70,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R; import com.android.internal.R;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -112,20 +117,27 @@ public class AuthControllerTest extends SysuiTestCase {
private SidefpsController mSidefpsController; private SidefpsController mSidefpsController;
@Mock @Mock
private DisplayManager mDisplayManager; private DisplayManager mDisplayManager;
@Mock
private Handler mHandler;
@Captor @Captor
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor; ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
@Captor
ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;
private TestableContext mContextSpy;
private Execution mExecution;
private TestableLooper mTestableLooper;
private Handler mHandler;
private TestableAuthController mAuthController; private TestableAuthController mAuthController;
@Before @Before
public void setup() throws RemoteException { public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
TestableContext context = spy(mContext); mContextSpy = spy(mContext);
mExecution = new FakeExecution();
mTestableLooper = TestableLooper.get(this);
mHandler = new Handler(mTestableLooper.getLooper());
when(context.getPackageManager()).thenReturn(mPackageManager); when(mContextSpy.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(true); .thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
@@ -158,21 +170,78 @@ public class AuthControllerTest extends SysuiTestCase {
props.add(prop); props.add(prop);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
mAuthController = new TestableAuthController(context, mCommandQueue, mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
() -> mUdfpsController, () -> mSidefpsController); () -> mUdfpsController, () -> mSidefpsController);
mAuthController.start(); mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture()); mAuthenticatorsRegisteredCaptor.capture());
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props); mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
// Ensures that the operations posted on the handler get executed.
mTestableLooper.processAllMessages();
} }
// Callback tests // Callback tests
@Test
public void testRegistersFingerprintStateListener_afterAllAuthenticatorsAreRegistered()
throws RemoteException {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture());
mTestableLooper.processAllMessages();
verify(mFingerprintManager, never()).registerFingerprintStateListener(any());
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
mTestableLooper.processAllMessages();
verify(mFingerprintManager).registerFingerprintStateListener(any());
}
@Test
public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
mAuthenticatorsRegisteredCaptor.capture());
// Emulates a device with no authenticators (empty list).
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
mTestableLooper.processAllMessages();
verify(mFingerprintManager).registerFingerprintStateListener(
mFingerprintStateCaptor.capture());
// Enrollments changed for an unknown sensor.
mFingerprintStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */,
0xbeef /* sensorId */, true /* hasEnrollments */);
mTestableLooper.processAllMessages();
// Nothing should crash.
}
@Test @Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
null /* credentialAttestation */); null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed( verify(mReceiver).onDialogDismissed(
@@ -497,7 +566,7 @@ public class AuthControllerTest extends SysuiTestCase {
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
mAuthController.mTaskStackListener.onTaskStackChanged(); mAuthController.mTaskStackListener.onTaskStackChanged();
waitForIdleSync(); mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog); assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver); assertNull(mAuthController.mReceiver);
@@ -528,7 +597,7 @@ public class AuthControllerTest extends SysuiTestCase {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mAuthController.mBroadcastReceiver.onReceive(mContext, intent); mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
waitForIdleSync(); mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog); assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver); assertNull(mAuthController.mReceiver);
@@ -598,6 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
private PromptInfo mLastBiometricPromptInfo; private PromptInfo mLastBiometricPromptInfo;
TestableAuthController(Context context, TestableAuthController(Context context,
Execution execution,
CommandQueue commandQueue, CommandQueue commandQueue,
ActivityTaskManager activityTaskManager, ActivityTaskManager activityTaskManager,
WindowManager windowManager, WindowManager windowManager,
@@ -605,7 +675,7 @@ public class AuthControllerTest extends SysuiTestCase {
FaceManager faceManager, FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory, Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory) { Provider<SidefpsController> sidefpsControllerFactory) {
super(context, commandQueue, activityTaskManager, windowManager, super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory, fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mHandler); sidefpsControllerFactory, mDisplayManager, mHandler);
} }