Merge "Make UserTrackerImpl use UserSwitchObserver instead of broadcast." into tm-qpr-dev

This commit is contained in:
Yasin Kilicdere
2023-01-25 17:44:34 +00:00
committed by Android (Google) Code Review
7 changed files with 127 additions and 36 deletions

View File

@@ -62,12 +62,24 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
fun removeCallback(callback: Callback)
/**
* Ćallback for notifying of changes.
* Callback for notifying of changes.
*/
interface Callback {
/**
* Notifies that the current user is being changed.
* 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.
*/
@JvmDefault
fun onUserChanging(newUser: Int, userContext: Context) {}
/**
* Notifies that the current user has changed.
* Override this method to run things after the screen is unfrozen for the user switch.
* Please see {@link #onUserChanging} if you need to hide jank.
*/
@JvmDefault
fun onUserChanged(newUser: Int, userContext: Context) {}
@@ -78,4 +90,4 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
@JvmDefault
fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
}
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.settings
import android.app.IActivityManager
import android.app.UserSwitchObserver
import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Context
@@ -23,6 +25,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.content.pm.UserInfo
import android.os.Handler
import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
@@ -34,6 +37,7 @@ import com.android.systemui.util.Assert
import java.io.PrintWriter
import java.lang.IllegalStateException
import java.lang.ref.WeakReference
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -56,6 +60,7 @@ import kotlin.reflect.KProperty
class UserTrackerImpl internal constructor(
private val context: Context,
private val userManager: UserManager,
private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
private val backgroundHandler: Handler
) : UserTracker, Dumpable, BroadcastReceiver() {
@@ -107,7 +112,6 @@ class UserTrackerImpl internal constructor(
setUserIdInternal(startingUser)
val filter = IntentFilter().apply {
addAction(Intent.ACTION_USER_SWITCHED)
addAction(Intent.ACTION_USER_INFO_CHANGED)
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
@@ -118,14 +122,13 @@ class UserTrackerImpl internal constructor(
}
context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
registerUserSwitchObserver()
dumpManager.registerDumpable(TAG, this)
}
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_USER_SWITCHED -> {
handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
}
Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
@@ -157,22 +160,43 @@ class UserTrackerImpl internal constructor(
return ctx to profiles
}
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
backgroundHandler.run {
handleUserSwitching(newUserId)
reply?.sendResult(null)
}
}
override fun onUserSwitchComplete(newUserId: Int) {
backgroundHandler.run {
handleUserSwitchComplete(newUserId)
}
}
}, TAG)
}
@WorkerThread
private fun handleSwitchUser(newUser: Int) {
private fun handleUserSwitching(newUserId: Int) {
Assert.isNotMainThread()
if (newUser == UserHandle.USER_NULL) {
Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
return
}
if (newUser == userId) return
Log.i(TAG, "Switching to user $newUser")
val (ctx, profiles) = setUserIdInternal(newUser)
Log.i(TAG, "Switching to user $newUserId")
setUserIdInternal(newUserId)
notifySubscribers {
onUserChanged(newUser, ctx)
onProfilesChanged(profiles)
onUserChanging(newUserId, userContext)
}.await()
}
@WorkerThread
private fun handleUserSwitchComplete(newUserId: Int) {
Assert.isNotMainThread()
Log.i(TAG, "Switched to user $newUserId")
setUserIdInternal(newUserId)
notifySubscribers {
onUserChanged(newUserId, userContext)
onProfilesChanged(userProfiles)
}
}
@@ -201,17 +225,25 @@ class UserTrackerImpl internal constructor(
}
}
private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
private inline fun notifySubscribers(
crossinline action: UserTracker.Callback.() -> Unit
): CountDownLatch {
val list = synchronized(callbacks) {
callbacks.toList()
}
val latch = CountDownLatch(list.size)
list.forEach {
if (it.callback.get() != null) {
it.executor.execute {
it.callback.get()?.action()
latch.countDown()
}
} else {
latch.countDown()
}
}
return latch
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -258,4 +290,4 @@ private data class DataItem(
fun sameOrEmpty(other: UserTracker.Callback): Boolean {
return callback.get()?.equals(other) ?: true
}
}
}

View File

@@ -17,6 +17,7 @@
package com.android.systemui.settings.dagger;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.UserManager;
@@ -57,11 +58,13 @@ public abstract class MultiUserUtilsModule {
static UserTracker provideUserTracker(
Context context,
UserManager userManager,
IActivityManager iActivityManager,
DumpManager dumpManager,
@Background Handler handler
) {
int startingUser = ActivityManager.getCurrentUser();
UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
dumpManager, handler);
tracker.initialize(startingUser);
return tracker;
}

View File

@@ -174,7 +174,7 @@ constructor(
val callback =
object : UserTracker.Callback {
override fun onUserChanged(newUser: Int, userContext: Context) {
override fun onUserChanging(newUser: Int, userContext: Context) {
send()
}

View File

@@ -1,5 +1,6 @@
package com.android.systemui.settings
import android.app.IActivityManager
import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
@@ -51,6 +52,7 @@ class UserTrackerImplReceiveTest : SysuiTestCase() {
@Mock private lateinit var context: Context
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var iActivityManager: IActivityManager
@Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
@Mock(stubOnly = true) private lateinit var handler: Handler
@@ -67,7 +69,7 @@ class UserTrackerImplReceiveTest : SysuiTestCase() {
`when`(context.user).thenReturn(UserHandle.SYSTEM)
`when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test

View File

@@ -16,11 +16,14 @@
package com.android.systemui.settings
import android.app.IActivityManager
import android.app.IUserSwitchObserver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.UserInfo
import android.os.Handler
import android.os.IRemoteCallback
import android.os.UserHandle
import android.os.UserManager
import android.testing.AndroidTestingRunner
@@ -29,19 +32,20 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import java.util.concurrent.Executor
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -51,6 +55,10 @@ class UserTrackerImplTest : SysuiTestCase() {
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)
@@ -76,7 +84,7 @@ class UserTrackerImplTest : SysuiTestCase() {
listOf(info)
}
tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
}
@Test
@@ -125,8 +133,7 @@ class UserTrackerImplTest : SysuiTestCase() {
verify(context).registerReceiverForAllUsers(
eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
assertThat(countActions()).isEqualTo(7)
assertThat(hasAction(Intent.ACTION_USER_SWITCHED)).isTrue()
assertThat(countActions()).isEqualTo(6)
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
@@ -158,8 +165,10 @@ class UserTrackerImplTest : SysuiTestCase() {
tracker.initialize(0)
val newID = 5
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
tracker.onReceive(context, intent)
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitching(newID, userSwitchingReply)
verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -271,6 +280,24 @@ class UserTrackerImplTest : SysuiTestCase() {
assertThat(callback.calledOnUserChanged).isEqualTo(0)
}
@Test
fun testCallbackCalledOnUserChanging() {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
val newID = 5
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitching(newID, userSwitchingReply)
verify(userSwitchingReply).sendResult(any())
assertThat(callback.calledOnUserChanging).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
}
@Test
fun testCallbackCalledOnUserChanged() {
tracker.initialize(0)
@@ -279,8 +306,9 @@ class UserTrackerImplTest : SysuiTestCase() {
val newID = 5
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
tracker.onReceive(context, intent)
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitchComplete(newID)
assertThat(callback.calledOnUserChanged).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -330,25 +358,36 @@ class UserTrackerImplTest : SysuiTestCase() {
tracker.addCallback(callback, executor)
tracker.removeCallback(callback)
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
tracker.onReceive(context, intent)
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
captor.value.onUserSwitching(newID, userSwitchingReply)
verify(userSwitchingReply).sendResult(any())
captor.value.onUserSwitchComplete(newID)
val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
.putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
tracker.onReceive(context, intentProfiles)
assertThat(callback.calledOnUserChanging).isEqualTo(0)
assertThat(callback.calledOnUserChanged).isEqualTo(0)
assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
}
private class TestCallback : UserTracker.Callback {
var calledOnUserChanging = 0
var calledOnUserChanged = 0
var calledOnProfilesChanged = 0
var lastUser: Int? = null
var lastUserContext: Context? = null
var lastUserProfiles = emptyList<UserInfo>()
override fun onUserChanging(newUser: Int, userContext: Context) {
calledOnUserChanging++
lastUser = newUser
lastUserContext = userContext
}
override fun onUserChanged(newUser: Int, userContext: Context) {
calledOnUserChanged++
lastUser = newUser
@@ -360,4 +399,4 @@ class UserTrackerImplTest : SysuiTestCase() {
lastUserProfiles = profiles
}
}
}
}

View File

@@ -67,7 +67,10 @@ class FakeUserTracker(
_userHandle = UserHandle.of(_userId)
val copy = callbacks.toList()
copy.forEach { it.onUserChanged(_userId, userContext) }
copy.forEach {
it.onUserChanging(_userId, userContext)
it.onUserChanged(_userId, userContext)
}
}
fun onProfileChanged() {