Merge "Fix mismatched state between SystemUI and FingerprintService" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
255aae7392
@@ -49,7 +49,6 @@ import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
|
||||
import android.hardware.fingerprint.IUdfpsHbmListener;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
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.doze.DozeReceiver;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.util.concurrency.Execution;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 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 ActivityTaskManager mActivityTaskManager;
|
||||
@Nullable private final FingerprintManager mFingerprintManager;
|
||||
@Nullable private final FaceManager mFaceManager;
|
||||
@Nullable
|
||||
private final FingerprintManager mFingerprintManager;
|
||||
@Nullable
|
||||
private final FaceManager mFaceManager;
|
||||
private final Provider<UdfpsController> mUdfpsControllerFactory;
|
||||
private final Provider<SidefpsController> mSidefpsControllerFactory;
|
||||
@Nullable private final PointF mFaceAuthSensorLocation;
|
||||
@Nullable private PointF mFingerprintLocation;
|
||||
@Nullable
|
||||
private final PointF mFaceAuthSensorLocation;
|
||||
@Nullable
|
||||
private PointF mFingerprintLocation;
|
||||
private final Set<Callback> mCallbacks = new HashSet<>();
|
||||
|
||||
// TODO: These should just be saved from onSaveState
|
||||
@@ -133,62 +138,27 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
}
|
||||
}
|
||||
|
||||
private final FingerprintStateListener mFingerprintStateListener =
|
||||
new FingerprintStateListener() {
|
||||
@Override
|
||||
public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
|
||||
Log.d(TAG, "onEnrollmentsChanged, userId: " + userId
|
||||
+ ", sensorId: " + sensorId
|
||||
+ ", hasEnrollments: " + hasEnrollments);
|
||||
for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
|
||||
if (prop.sensorId == sensorId) {
|
||||
mUdfpsEnrolledForUser.put(userId, hasEnrollments);
|
||||
}
|
||||
}
|
||||
|
||||
for (Callback cb : mCallbacks) {
|
||||
cb.onEnrollmentsChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
private final IFingerprintAuthenticatorsRegisteredCallback
|
||||
mFingerprintAuthenticatorsRegisteredCallback =
|
||||
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
|
||||
@Override public void onAllAuthenticatorsRegistered(
|
||||
@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();
|
||||
}
|
||||
mHandler.post(() -> handleAllAuthenticatorsRegistered(sensors));
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
private final FingerprintStateListener mFingerprintStateListener =
|
||||
new FingerprintStateListener() {
|
||||
@Override
|
||||
public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
|
||||
mHandler.post(
|
||||
() -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (mCurrentDialog != null
|
||||
@@ -212,6 +182,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
};
|
||||
|
||||
private void handleTaskStackChanged() {
|
||||
mExecution.assertIsMainThread();
|
||||
if (mCurrentDialog != null) {
|
||||
try {
|
||||
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}.
|
||||
*/
|
||||
@@ -449,6 +470,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
|
||||
@Inject
|
||||
public AuthController(Context context,
|
||||
Execution execution,
|
||||
CommandQueue commandQueue,
|
||||
ActivityTaskManager activityTaskManager,
|
||||
@NonNull WindowManager windowManager,
|
||||
@@ -459,6 +481,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
@NonNull DisplayManager displayManager,
|
||||
@Main Handler handler) {
|
||||
super(context);
|
||||
mExecution = execution;
|
||||
mHandler = handler;
|
||||
mCommandQueue = commandQueue;
|
||||
mActivityTaskManager = activityTaskManager;
|
||||
mFingerprintManager = fingerprintManager;
|
||||
@@ -470,7 +494,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
mOrientationListener = new BiometricDisplayListener(
|
||||
context,
|
||||
displayManager,
|
||||
handler,
|
||||
mHandler,
|
||||
BiometricDisplayListener.SensorType.Generic.INSTANCE,
|
||||
() -> {
|
||||
onOrientationChanged();
|
||||
@@ -521,7 +545,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
if (mFingerprintManager != null) {
|
||||
mFingerprintManager.addAuthenticatorsRegisteredCallback(
|
||||
mFingerprintAuthenticatorsRegisteredCallback);
|
||||
mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
|
||||
}
|
||||
|
||||
mTaskStackListener = new BiometricTaskStackListener();
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -53,12 +54,14 @@ import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.hardware.fingerprint.FingerprintStateListener;
|
||||
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableContext;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -67,6 +70,8 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.internal.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
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.Test;
|
||||
@@ -112,20 +117,27 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
private SidefpsController mSidefpsController;
|
||||
@Mock
|
||||
private DisplayManager mDisplayManager;
|
||||
@Mock
|
||||
private Handler mHandler;
|
||||
@Captor
|
||||
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
|
||||
@Captor
|
||||
ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;
|
||||
|
||||
private TestableContext mContextSpy;
|
||||
private Execution mExecution;
|
||||
private TestableLooper mTestableLooper;
|
||||
private Handler mHandler;
|
||||
private TestableAuthController mAuthController;
|
||||
|
||||
@Before
|
||||
public void setup() throws RemoteException {
|
||||
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))
|
||||
.thenReturn(true);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
|
||||
@@ -158,21 +170,78 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
props.add(prop);
|
||||
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
|
||||
|
||||
mAuthController = new TestableAuthController(context, mCommandQueue,
|
||||
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
|
||||
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
|
||||
() -> mUdfpsController, () -> mSidefpsController);
|
||||
|
||||
mAuthController.start();
|
||||
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
|
||||
mAuthenticatorsRegisteredCaptor.capture());
|
||||
|
||||
mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
|
||||
|
||||
// Ensures that the operations posted on the handler get executed.
|
||||
mTestableLooper.processAllMessages();
|
||||
}
|
||||
|
||||
// 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
|
||||
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,
|
||||
null /* credentialAttestation */);
|
||||
verify(mReceiver).onDialogDismissed(
|
||||
@@ -497,7 +566,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
|
||||
|
||||
mAuthController.mTaskStackListener.onTaskStackChanged();
|
||||
waitForIdleSync();
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mAuthController.mCurrentDialog);
|
||||
assertNull(mAuthController.mReceiver);
|
||||
@@ -528,7 +597,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
|
||||
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||
mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
|
||||
waitForIdleSync();
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mAuthController.mCurrentDialog);
|
||||
assertNull(mAuthController.mReceiver);
|
||||
@@ -598,6 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
private PromptInfo mLastBiometricPromptInfo;
|
||||
|
||||
TestableAuthController(Context context,
|
||||
Execution execution,
|
||||
CommandQueue commandQueue,
|
||||
ActivityTaskManager activityTaskManager,
|
||||
WindowManager windowManager,
|
||||
@@ -605,7 +675,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
FaceManager faceManager,
|
||||
Provider<UdfpsController> udfpsControllerFactory,
|
||||
Provider<SidefpsController> sidefpsControllerFactory) {
|
||||
super(context, commandQueue, activityTaskManager, windowManager,
|
||||
super(context, execution, commandQueue, activityTaskManager, windowManager,
|
||||
fingerprintManager, faceManager, udfpsControllerFactory,
|
||||
sidefpsControllerFactory, mDisplayManager, mHandler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user