summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt145
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt4
9 files changed, 238 insertions, 68 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0f733c6e1f24..422b2e1a1d28 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -203,7 +203,6 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -2440,7 +2439,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
handleDevicePolicyManagerStateChanged(msg.arg1);
break;
case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj);
+ handleUserSwitching(msg.arg1, (Runnable) msg.obj);
break;
case MSG_USER_SWITCH_COMPLETE:
handleUserSwitchComplete(msg.arg1);
@@ -2741,10 +2740,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
+
@Override
- public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) {
+ public void onUserChanging(int newUser, @NonNull Context userContext,
+ @NonNull Runnable resultCallback) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
- newUser, 0, latch));
+ newUser, 0, resultCallback));
}
@Override
@@ -3575,7 +3576,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* Handle {@link #MSG_USER_SWITCHING}
*/
@VisibleForTesting
- void handleUserSwitching(int userId, CountDownLatch latch) {
+ void handleUserSwitching(int userId, Runnable resultCallback) {
mLogger.logUserSwitching(userId, "from UserTracker");
Assert.isMainThread();
clearBiometricRecognized();
@@ -3589,7 +3590,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onUserSwitching(userId);
}
}
- latch.countDown();
+ resultCallback.run();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cef52e73d711..d0ceaf6075fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -117,7 +117,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -492,9 +491,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
notifySystemUiStateFlags(mSysUiState.getFlags());
notifyConnectionChanged();
- if (mLatchForOnUserChanging != null) {
- mLatchForOnUserChanging.countDown();
- mLatchForOnUserChanging = null;
+ if (mDoneUserChanging != null) {
+ mDoneUserChanging.run();
+ mDoneUserChanging = null;
}
}
@@ -550,14 +549,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
};
- private CountDownLatch mLatchForOnUserChanging;
+ private Runnable mDoneUserChanging;
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
public void onUserChanging(int newUser, @NonNull Context userContext,
- CountDownLatch latch) {
+ @NonNull Runnable resultCallback) {
mConnectionBackoffAttempts = 0;
- mLatchForOnUserChanging = latch;
+ mDoneUserChanging = resultCallback;
internalConnectToCurrentUser("User changed");
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 93a3e905f0e0..bd592c9daff6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -19,7 +19,6 @@ package com.android.systemui.settings
import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
/**
@@ -68,8 +67,8 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
interface Callback {
/**
- * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
- * auto-decremented after the completion of this method.
+ * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
+ * called automatically after the completion of this method.
*/
fun onUserChanging(newUser: Int, userContext: Context) {}
@@ -78,12 +77,12 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* Override this method to run things while the screen is frozen for the user switch.
* Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
* screen further. Please be aware that code executed in this callback will lengthen the
- * user switch duration. When overriding this method, countDown() MUST be called on the
- * latch once execution is complete.
+ * user switch duration. When overriding this method, resultCallback#run() MUST be called
+ * once the execution is complete.
*/
- fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
+ fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) {
onUserChanging(newUser, userContext)
- latch.countDown()
+ resultCallback.run()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 5fb3c01bbc1d..2f873019349c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -33,11 +33,22 @@ import androidx.annotation.GuardedBy
import androidx.annotation.WorkerThread
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.util.Assert
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
import java.io.PrintWriter
import java.lang.ref.WeakReference
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
+import javax.inject.Provider
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -58,20 +69,26 @@ import kotlin.reflect.KProperty
*/
open class UserTrackerImpl internal constructor(
private val context: Context,
+ private val featureFlagsProvider: Provider<FeatureFlagsClassic>,
private val userManager: UserManager,
private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
- private val backgroundHandler: Handler
+ private val appScope: CoroutineScope,
+ private val backgroundContext: CoroutineDispatcher,
+ private val backgroundHandler: Handler,
) : UserTracker, Dumpable, BroadcastReceiver() {
companion object {
private const val TAG = "UserTrackerImpl"
+ private const val USER_CHANGE_THRESHOLD = 5L * 1000 // 5 sec
}
var initialized = false
private set
private val mutex = Any()
+ private val isBackgroundUserSwitchEnabled: Boolean get() =
+ featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
override var userId: Int by SynchronizedDelegate(context.userId)
protected set
@@ -103,6 +120,10 @@ open class UserTrackerImpl internal constructor(
@GuardedBy("callbacks")
private val callbacks: MutableList<DataItem> = ArrayList()
+ private var beforeUserSwitchingJob: Job? = null
+ private var userSwitchingJob: Job? = null
+ private var afterUserSwitchingJob: Job? = null
+
open fun initialize(startingUser: Int) {
if (initialized) {
return
@@ -119,7 +140,7 @@ open class UserTrackerImpl internal constructor(
addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
}
- context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+ context.registerReceiverForAllUsers(this, filter, null, backgroundHandler)
registerUserSwitchObserver()
@@ -162,16 +183,39 @@ open class UserTrackerImpl internal constructor(
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
override fun onBeforeUserSwitching(newUserId: Int) {
- handleBeforeUserSwitching(newUserId)
+ if (isBackgroundUserSwitchEnabled) {
+ beforeUserSwitchingJob?.cancel()
+ beforeUserSwitchingJob = appScope.launch(backgroundContext) {
+ handleBeforeUserSwitching(newUserId)
+ }
+ } else {
+ handleBeforeUserSwitching(newUserId)
+ }
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
- handleUserSwitching(newUserId)
- reply?.sendResult(null)
+ if (isBackgroundUserSwitchEnabled) {
+ userSwitchingJob?.cancel()
+ userSwitchingJob = appScope.launch(backgroundContext) {
+ handleUserSwitchingCoroutines(newUserId) {
+ reply?.sendResult(null)
+ }
+ }
+ } else {
+ handleUserSwitching(newUserId)
+ reply?.sendResult(null)
+ }
}
override fun onUserSwitchComplete(newUserId: Int) {
- handleUserSwitchComplete(newUserId)
+ if (isBackgroundUserSwitchEnabled) {
+ afterUserSwitchingJob?.cancel()
+ afterUserSwitchingJob = appScope.launch(backgroundContext) {
+ handleUserSwitchComplete(newUserId)
+ }
+ } else {
+ handleUserSwitchComplete(newUserId)
+ }
}
}, TAG)
}
@@ -195,7 +239,7 @@ open class UserTrackerImpl internal constructor(
val callback = it.callback.get()
if (callback != null) {
it.executor.execute {
- callback.onUserChanging(userId, userContext, latch)
+ callback.onUserChanging(userId, userContext) { latch.countDown() }
}
} else {
latch.countDown()
@@ -205,6 +249,28 @@ open class UserTrackerImpl internal constructor(
}
@WorkerThread
+ protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) =
+ coroutineScope {
+ Assert.isNotMainThread()
+ Log.i(TAG, "Switching to user $newUserId")
+
+ for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
+ val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
+ launch(callbackDataItem.executor.asCoroutineDispatcher()) {
+ val mutex = Mutex(true)
+ val thresholdLogJob = launch(backgroundContext) {
+ delay(USER_CHANGE_THRESHOLD)
+ Log.e(TAG, "Failed to finish $callback in time")
+ }
+ callback.onUserChanging(userId, userContext) { mutex.unlock() }
+ mutex.lock()
+ thresholdLogJob.cancel()
+ }.join()
+ }
+ onDone()
+ }
+
+ @WorkerThread
protected open fun handleUserSwitchComplete(newUserId: Int) {
Assert.isNotMainThread()
Log.i(TAG, "Switched to user $newUserId")
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index e9a1dd7e6ecb..a0dd924b3959 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -25,8 +25,10 @@ import android.os.UserManager;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.DisplayTrackerImpl;
import com.android.systemui.settings.UserContentResolverProvider;
@@ -42,6 +44,11 @@ import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import javax.inject.Provider;
+
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
+
/**
* Dagger Module for classes found within the com.android.systemui.settings package.
*/
@@ -60,14 +67,17 @@ public abstract class MultiUserUtilsModule {
@Provides
static UserTracker provideUserTracker(
Context context,
+ Provider<FeatureFlagsClassic> featureFlagsProvider,
UserManager userManager,
IActivityManager iActivityManager,
DumpManager dumpManager,
+ @Application CoroutineScope appScope,
+ @Background CoroutineDispatcher backgroundDispatcher,
@Background Handler handler
) {
int startingUser = ActivityManager.getCurrentUser();
- UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
- dumpManager, handler);
+ UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
+ iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
tracker.initialize(startingUser);
return tracker;
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 562d3a557ea2..29511329b630 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -172,7 +172,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -1363,7 +1362,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
- mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, new CountDownLatch(0));
+ mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {});
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index beb981d89735..1bb2ff825115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -11,10 +11,13 @@ import androidx.concurrent.futures.DirectExecutor
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,6 +63,9 @@ class UserTrackerImplReceiveTest : SysuiTestCase() {
@Mock private lateinit var callback: UserTracker.Callback
@Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
+ private val fakeFeatures = FakeFeatureFlagsClassic()
+ private val testDispatcher = StandardTestDispatcher()
+
private lateinit var tracker: UserTrackerImpl
@Before
@@ -68,12 +74,21 @@ class UserTrackerImplReceiveTest : SysuiTestCase() {
`when`(context.user).thenReturn(UserHandle.SYSTEM)
`when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
-
- tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test
- fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() {
+ fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() = runTest {
+ tracker =
+ UserTrackerImpl(
+ context,
+ { fakeFeatures },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ this,
+ testDispatcher,
+ handler
+ )
tracker.initialize(0)
tracker.addCallback(callback, executor)
val profileID = tracker.userId + 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index aa98f08e9015..52b25f85b870 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -26,16 +26,25 @@ import android.os.Handler
import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
-import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Executor
+import com.google.common.truth.TruthJUnit.assume
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
@@ -43,28 +52,52 @@ import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(Parameterized::class)
class UserTrackerImplTest : SysuiTestCase() {
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false)
+ }
+
@Mock
private lateinit var context: Context
+
@Mock
private lateinit var userManager: UserManager
+
@Mock
private lateinit var iActivityManager: IActivityManager
+
@Mock
private lateinit var userSwitchingReply: IRemoteCallback
+
@Mock(stubOnly = true)
private lateinit var dumpManager: DumpManager
+
@Mock(stubOnly = true)
private lateinit var handler: Handler
+ @Parameterized.Parameter
+ @JvmField
+ var isBackgroundUserTrackerEnabled: Boolean = false
+
+ private val testScope = TestScope()
+ private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
private val executor = Executor(Runnable::run)
+ private val featureFlags = FakeFeatureFlagsClassic()
+
private lateinit var tracker: UserTrackerImpl
@Before
@@ -84,54 +117,65 @@ class UserTrackerImplTest : SysuiTestCase() {
listOf(info)
}
- tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
+ featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled)
+ tracker =
+ UserTrackerImpl(
+ context,
+ { featureFlags },
+ userManager,
+ iActivityManager,
+ dumpManager,
+ testScope.backgroundScope,
+ testDispatcher,
+ handler,
+ )
}
@Test
- fun testNotInitialized() {
+ fun testNotInitialized() = testScope.runTest {
assertThat(tracker.initialized).isFalse()
}
@Test(expected = IllegalStateException::class)
- fun testGetUserIdBeforeInitThrowsException() {
+ fun testGetUserIdBeforeInitThrowsException() = testScope.runTest {
tracker.userId
}
@Test(expected = IllegalStateException::class)
- fun testGetUserHandleBeforeInitThrowsException() {
+ fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest {
tracker.userHandle
}
@Test(expected = IllegalStateException::class)
- fun testGetUserContextBeforeInitThrowsException() {
+ fun testGetUserContextBeforeInitThrowsException() = testScope.runTest {
tracker.userContext
}
@Test(expected = IllegalStateException::class)
- fun testGetUserContentResolverBeforeInitThrowsException() {
+ fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest {
tracker.userContentResolver
}
@Test(expected = IllegalStateException::class)
- fun testGetUserProfilesBeforeInitThrowsException() {
+ fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest {
tracker.userProfiles
}
@Test
- fun testInitialize() {
+ fun testInitialize() = testScope.runTest {
tracker.initialize(0)
assertThat(tracker.initialized).isTrue()
}
@Test
- fun testReceiverRegisteredOnInitialize() {
+ fun testReceiverRegisteredOnInitialize() = testScope.runTest {
tracker.initialize(0)
val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
- verify(context).registerReceiverForAllUsers(
- eq(tracker), capture(captor), isNull(), eq(handler))
+ verify(context)
+ .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
assertThat(countActions()).isEqualTo(6)
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
@@ -144,7 +188,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testInitialValuesSet() {
+ fun testInitialValuesSet() = testScope.runTest {
val testID = 4
tracker.initialize(testID)
@@ -161,7 +205,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testUserSwitch() {
+ fun testUserSwitch() = testScope.runTest {
tracker.initialize(0)
val newID = 5
@@ -169,6 +213,7 @@ class UserTrackerImplTest : SysuiTestCase() {
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -184,7 +229,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testManagedProfileAvailable() {
+ fun testManagedProfileAvailable() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -209,7 +254,8 @@ class UserTrackerImplTest : SysuiTestCase() {
assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
}
- fun testManagedProfileUnavailable() {
+ @Test
+ fun testManagedProfileUnavailable() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -234,7 +280,8 @@ class UserTrackerImplTest : SysuiTestCase() {
assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
}
- fun testManagedProfileStartedAndRemoved() {
+ @Test
+ fun testManagedProfileStartedAndRemoved() = testScope.runTest {
tracker.initialize(0)
val profileID = tracker.userId + 10
@@ -271,7 +318,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testCallbackNotCalledOnAdd() {
+ fun testCallbackNotCalledOnAdd() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
@@ -282,7 +329,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testCallbackCalledOnUserChanging() {
+ fun testCallbackCalledOnUserChanging() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -293,15 +340,49 @@ class UserTrackerImplTest : SysuiTestCase() {
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
- verify(userSwitchingReply).sendResult(any())
+ runCurrent()
+ verify(userSwitchingReply).sendResult(any())
assertThat(callback.calledOnUserChanging).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
}
@Test
- fun testCallbackCalledOnUserChanged() {
+ fun testAsyncCallbackWaitsUserToChange() = testScope.runTest {
+ // Skip this test for CountDownLatch variation. The problem is that there would be a
+ // deadlock if the callbacks processing runs on the same thread as the callback (which
+ // is blocked by the latch). Before the change it works because the callbacks are
+ // processed on a binder thread which is always distinct.
+ // This is the issue that this feature addresses.
+ assume().that(isBackgroundUserTrackerEnabled).isTrue()
+
+ tracker.initialize(0)
+ val callback = TestCallback()
+ val callbackExecutor = FakeExecutor(FakeSystemClock())
+ tracker.addCallback(callback, callbackExecutor)
+
+ val newID = 5
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID)
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(0)
+ verify(userSwitchingReply, never()).sendResult(any())
+
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+ FakeExecutor.exhaustExecutors(callbackExecutor)
+ runCurrent()
+
+ assertThat(callback.calledOnUserChanging).isEqualTo(1)
+ verify(userSwitchingReply).sendResult(any())
+ }
+
+ @Test
+ fun testCallbackCalledOnUserChanged() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -312,6 +393,7 @@ class UserTrackerImplTest : SysuiTestCase() {
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitchComplete(newID)
+ runCurrent()
assertThat(callback.calledOnUserChanged).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -321,7 +403,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testCallbackCalledOnUserInfoChanged() {
+ fun testCallbackCalledOnUserInfoChanged() = testScope.runTest {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
@@ -331,18 +413,18 @@ class UserTrackerImplTest : SysuiTestCase() {
val id = invocation.getArgument<Int>(0)
val info = UserInfo(id, "", UserInfo.FLAG_FULL)
val infoProfile = UserInfo(
- id + 10,
- "",
- "",
- UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
}
val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
tracker.onReceive(context, intent)
@@ -352,7 +434,7 @@ class UserTrackerImplTest : SysuiTestCase() {
}
@Test
- fun testCallbackRemoved() {
+ fun testCallbackRemoved() = testScope.runTest {
tracker.initialize(0)
val newID = 5
val profileID = newID + 10
@@ -364,6 +446,7 @@ class UserTrackerImplTest : SysuiTestCase() {
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitching(newID, userSwitchingReply)
+ runCurrent()
verify(userSwitchingReply).sendResult(any())
captor.value.onUserSwitchComplete(newID)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 4242c1635468..f5f924dfce4b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -22,7 +22,6 @@ import android.content.pm.UserInfo
import android.os.UserHandle
import android.test.mock.MockContentResolver
import com.android.systemui.util.mockito.mock
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
/** A fake [UserTracker] to be used in tests. */
@@ -73,8 +72,7 @@ class FakeUserTracker(
fun onUserChanging(userId: Int = _userId) {
val copy = callbacks.toList()
- val latch = CountDownLatch(copy.size)
- copy.forEach { it.onUserChanging(userId, userContext, latch) }
+ copy.forEach { it.onUserChanging(userId, userContext) {} }
}
fun onUserChanged(userId: Int = _userId) {