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:
Joshua Mccloskey
2022-06-13 21:33:52 +00:00
committed by Automerger Merge Worker
2 changed files with 66 additions and 4 deletions

View File

@@ -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;
}
} }

View File

@@ -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