Merge "UserAwareBiometricScheduler lifecycle fix." into tm-d1-dev am: 9a277a47e7
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18822570 Change-Id: I2ca29b9c2157a271748bc6fb45361b1c942d84ff Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -67,6 +67,14 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
|
|||||||
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
|
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
|
Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
|
||||||
|
|
||||||
|
// Set mStopUserClient to null when StopUserClient fails. Otherwise it's possible
|
||||||
|
// for that the queue will wait indefinitely until the field is cleared.
|
||||||
|
if (clientMonitor instanceof StopUserClient<?> && !success) {
|
||||||
|
Slog.w(getTag(),
|
||||||
|
"StopUserClient failed(), is the HAL stuck? Clearing mStopUserClient");
|
||||||
|
mStopUserClient = null;
|
||||||
|
}
|
||||||
if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
|
if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
|
||||||
mCurrentOperation = null;
|
mCurrentOperation = null;
|
||||||
} else {
|
} else {
|
||||||
@@ -166,4 +174,9 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
|
|||||||
mStopUserClient.onUserStopped();
|
mStopUserClient.onUserStopped();
|
||||||
mStopUserClient = null;
|
mStopUserClient = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@Nullable public StopUserClient<?> getStopUserClient() {
|
||||||
|
return mStopUserClient;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ public class UserAwareBiometricSchedulerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BiometricContext mBiometricContext;
|
private BiometricContext mBiometricContext;
|
||||||
|
|
||||||
|
private boolean mShouldFailStopUser = false;
|
||||||
|
private final StopUserClientShouldFail mStopUserClientShouldFail =
|
||||||
|
() -> {
|
||||||
|
return mShouldFailStopUser;
|
||||||
|
};
|
||||||
private final TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
|
private final TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
|
||||||
private final TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
|
private final TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
|
||||||
private int mCurrentUserId = UserHandle.USER_NULL;
|
private int mCurrentUserId = UserHandle.USER_NULL;
|
||||||
@@ -88,6 +93,7 @@ public class UserAwareBiometricSchedulerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mShouldFailStopUser = false;
|
||||||
mHandler = new Handler(TestableLooper.get(this).getLooper());
|
mHandler = new Handler(TestableLooper.get(this).getLooper());
|
||||||
mScheduler = new UserAwareBiometricScheduler(TAG,
|
mScheduler = new UserAwareBiometricScheduler(TAG,
|
||||||
mHandler,
|
mHandler,
|
||||||
@@ -101,7 +107,7 @@ public class UserAwareBiometricSchedulerTest {
|
|||||||
public StopUserClient<?> getStopUserClient(int userId) {
|
public StopUserClient<?> getStopUserClient(int userId) {
|
||||||
return new TestStopUserClient(mContext, Object::new, mToken, userId,
|
return new TestStopUserClient(mContext, Object::new, mToken, userId,
|
||||||
TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
|
TEST_SENSOR_ID, mBiometricLogger, mBiometricContext,
|
||||||
mUserStoppedCallback);
|
mUserStoppedCallback, mStopUserClientShouldFail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -240,6 +246,36 @@ public class UserAwareBiometricSchedulerTest {
|
|||||||
assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1);
|
assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartUser_failsClearsStopUserClient() {
|
||||||
|
// When a stop user client fails, check that mStopUserClient
|
||||||
|
// is set to null to prevent the scheduler from getting stuck.
|
||||||
|
BaseClientMonitor nextClient = mock(BaseClientMonitor.class);
|
||||||
|
when(nextClient.getTargetUserId()).thenReturn(10);
|
||||||
|
|
||||||
|
mScheduler.scheduleClientMonitor(nextClient);
|
||||||
|
|
||||||
|
waitForIdle();
|
||||||
|
verify(nextClient).start(any());
|
||||||
|
|
||||||
|
// finish first operation
|
||||||
|
mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
// schedule second operation but swap out the current operation
|
||||||
|
// before it runs so that it's not current when it's completion callback runs
|
||||||
|
nextClient = mock(BaseClientMonitor.class);
|
||||||
|
when(nextClient.getTargetUserId()).thenReturn(11);
|
||||||
|
mUserStartedCallback.mAfterStart = () -> mScheduler.mCurrentOperation = null;
|
||||||
|
mShouldFailStopUser = true;
|
||||||
|
mScheduler.scheduleClientMonitor(nextClient);
|
||||||
|
|
||||||
|
waitForIdle();
|
||||||
|
assertThat(mUserStartedCallback.mStartedUsers).containsExactly(10, 11).inOrder();
|
||||||
|
assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0);
|
||||||
|
assertThat(mScheduler.getStopUserClient()).isEqualTo(null);
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForIdle() {
|
private void waitForIdle() {
|
||||||
TestableLooper.get(this).processAllMessages();
|
TestableLooper.get(this).processAllMessages();
|
||||||
}
|
}
|
||||||
@@ -268,13 +304,19 @@ public class UserAwareBiometricSchedulerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TestStopUserClient extends StopUserClient<Object> {
|
private interface StopUserClientShouldFail {
|
||||||
|
boolean shouldFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestStopUserClient extends StopUserClient<Object> {
|
||||||
|
private StopUserClientShouldFail mShouldFailClient;
|
||||||
public TestStopUserClient(@NonNull Context context,
|
public TestStopUserClient(@NonNull Context context,
|
||||||
@NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
|
@NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId,
|
||||||
int sensorId, @NonNull BiometricLogger logger,
|
int sensorId, @NonNull BiometricLogger logger,
|
||||||
@NonNull BiometricContext biometricContext,
|
@NonNull BiometricContext biometricContext,
|
||||||
@NonNull UserStoppedCallback callback) {
|
@NonNull UserStoppedCallback callback, StopUserClientShouldFail shouldFail) {
|
||||||
super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
|
super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback);
|
||||||
|
mShouldFailClient = shouldFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -285,7 +327,14 @@ public class UserAwareBiometricSchedulerTest {
|
|||||||
@Override
|
@Override
|
||||||
public void start(@NonNull ClientMonitorCallback callback) {
|
public void start(@NonNull ClientMonitorCallback callback) {
|
||||||
super.start(callback);
|
super.start(callback);
|
||||||
onUserStopped();
|
if (mShouldFailClient.shouldFail()) {
|
||||||
|
getCallback().onClientFinished(this, false /* success */);
|
||||||
|
// When the above fails, it means that the HAL has died, in this case we
|
||||||
|
// need to ensure the UserSwitchCallback correctly returns the NULL user handle.
|
||||||
|
mCurrentUserId = UserHandle.USER_NULL;
|
||||||
|
} else {
|
||||||
|
onUserStopped();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user