Merge "Controls UI - Multi-user switch updates" into rvc-dev am: 7604a184d9 am: 44f3e98c7b

Change-Id: Ibcee0ba960526060693218d7868164e8d18bf143
This commit is contained in:
Matt Pietal
2020-05-26 12:11:26 +00:00
committed by Automerger Merge Worker
4 changed files with 49 additions and 49 deletions

View File

@@ -72,6 +72,9 @@ class ControlsControllerImpl @Inject constructor (
private const val USER_CHANGE_RETRY_DELAY = 500L // ms private const val USER_CHANGE_RETRY_DELAY = 500L // ms
private const val DEFAULT_ENABLED = 1 private const val DEFAULT_ENABLED = 1
private const val PERMISSION_SELF = "com.android.systemui.permission.SELF" private const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
private fun isAvailable(userId: Int, cr: ContentResolver) = Settings.Secure.getIntForUser(
cr, CONTROLS_AVAILABLE, DEFAULT_ENABLED, userId) != 0
} }
private var userChanging: Boolean = true private var userChanging: Boolean = true
@@ -85,8 +88,7 @@ class ControlsControllerImpl @Inject constructor (
private val contentResolver: ContentResolver private val contentResolver: ContentResolver
get() = context.contentResolver get() = context.contentResolver
override var available = Settings.Secure.getIntForUser( override var available = isAvailable(currentUserId, contentResolver)
contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, currentUserId) != 0
private set private set
private val persistenceWrapper: ControlsFavoritePersistenceWrapper private val persistenceWrapper: ControlsFavoritePersistenceWrapper
@@ -119,8 +121,7 @@ class ControlsControllerImpl @Inject constructor (
BackupManager(userStructure.userContext) BackupManager(userStructure.userContext)
) )
auxiliaryPersistenceWrapper.changeFile(userStructure.auxiliaryFile) auxiliaryPersistenceWrapper.changeFile(userStructure.auxiliaryFile)
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, available = isAvailable(newUser.identifier, contentResolver)
DEFAULT_ENABLED, newUser.identifier) != 0
resetFavorites(available) resetFavorites(available)
bindingController.changeUser(newUser) bindingController.changeUser(newUser)
listingController.changeUser(newUser) listingController.changeUser(newUser)
@@ -131,7 +132,6 @@ class ControlsControllerImpl @Inject constructor (
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_USER_SWITCHED) { if (intent.action == Intent.ACTION_USER_SWITCHED) {
userChanging = true userChanging = true
listingController.removeCallback(listingCallback)
val newUser = val newUser =
UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId)) UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
if (currentUser == newUser) { if (currentUser == newUser) {
@@ -151,7 +151,6 @@ class ControlsControllerImpl @Inject constructor (
executor.execute { executor.execute {
Log.d(TAG, "Restore finished, storing auxiliary favorites") Log.d(TAG, "Restore finished, storing auxiliary favorites")
auxiliaryPersistenceWrapper.initialize() auxiliaryPersistenceWrapper.initialize()
listingController.removeCallback(listingCallback)
persistenceWrapper.storeFavorites(auxiliaryPersistenceWrapper.favorites) persistenceWrapper.storeFavorites(auxiliaryPersistenceWrapper.favorites)
resetFavorites(available) resetFavorites(available)
} }
@@ -172,8 +171,7 @@ class ControlsControllerImpl @Inject constructor (
if (userChanging || userId != currentUserId) { if (userChanging || userId != currentUserId) {
return return
} }
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, available = isAvailable(currentUserId, contentResolver)
DEFAULT_ENABLED, currentUserId) != 0
resetFavorites(available) resetFavorites(available)
} }
} }
@@ -244,6 +242,7 @@ class ControlsControllerImpl @Inject constructor (
null null
) )
contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL) contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL)
listingController.addCallback(listingCallback)
} }
fun destroy() { fun destroy() {
@@ -258,7 +257,6 @@ class ControlsControllerImpl @Inject constructor (
if (shouldLoad) { if (shouldLoad) {
Favorites.load(persistenceWrapper.readFavorites()) Favorites.load(persistenceWrapper.readFavorites())
listingController.addCallback(listingCallback)
} }
} }
@@ -569,12 +567,12 @@ class UserStructure(context: Context, user: UserHandle) {
val userContext = context.createContextAsUser(user, 0) val userContext = context.createContextAsUser(user, 0)
val file = Environment.buildPath( val file = Environment.buildPath(
context.filesDir, userContext.filesDir,
ControlsFavoritePersistenceWrapper.FILE_NAME ControlsFavoritePersistenceWrapper.FILE_NAME
) )
val auxiliaryFile = Environment.buildPath( val auxiliaryFile = Environment.buildPath(
context.filesDir, userContext.filesDir,
AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME
) )
} }

View File

@@ -29,6 +29,7 @@ import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import java.util.concurrent.Executor import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@@ -75,6 +76,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
private var availableComponents = emptySet<ComponentName>() private var availableComponents = emptySet<ComponentName>()
private var availableServices = emptyList<ServiceInfo>() private var availableServices = emptyList<ServiceInfo>()
private var userChangeInProgress = AtomicInteger(0)
override var currentUserId = ActivityManager.getCurrentUser() override var currentUserId = ActivityManager.getCurrentUser()
private set private set
@@ -85,6 +87,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
newServices.mapTo(mutableSetOf<ComponentName>(), { s -> s.getComponentName() }) newServices.mapTo(mutableSetOf<ComponentName>(), { s -> s.getComponentName() })
backgroundExecutor.execute { backgroundExecutor.execute {
if (userChangeInProgress.get() > 0) return@execute
if (!newComponents.equals(availableComponents)) { if (!newComponents.equals(availableComponents)) {
Log.d(TAG, "ServiceConfig reloaded, count: ${newComponents.size}") Log.d(TAG, "ServiceConfig reloaded, count: ${newComponents.size}")
availableComponents = newComponents availableComponents = newComponents
@@ -105,16 +108,11 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
} }
override fun changeUser(newUser: UserHandle) { override fun changeUser(newUser: UserHandle) {
backgroundExecutor.execute { userChangeInProgress.incrementAndGet()
serviceListing.setListening(false) serviceListing.setListening(false)
// Notify all callbacks in order to clear their existing state prior to attaching backgroundExecutor.execute {
// a new listener if (userChangeInProgress.decrementAndGet() == 0) {
availableServices = emptyList()
callbacks.forEach {
it.onServicesUpdated(emptyList())
}
currentUserId = newUser.identifier currentUserId = newUser.identifier
val contextForUser = context.createContextAsUser(newUser, 0) val contextForUser = context.createContextAsUser(newUser, 0)
serviceListing = serviceListingBuilder(contextForUser) serviceListing = serviceListingBuilder(contextForUser)
@@ -123,6 +121,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
serviceListing.reload() serviceListing.reload()
} }
} }
}
/** /**
* Adds a callback to this controller. * Adds a callback to this controller.
@@ -134,12 +133,18 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
*/ */
override fun addCallback(listener: ControlsListingController.ControlsListingCallback) { override fun addCallback(listener: ControlsListingController.ControlsListingCallback) {
backgroundExecutor.execute { backgroundExecutor.execute {
if (userChangeInProgress.get() > 0) {
// repost this event, as callers may rely on the initial callback from
// onServicesUpdated
addCallback(listener)
} else {
val services = getCurrentServices() val services = getCurrentServices()
Log.d(TAG, "Subscribing callback, service count: ${services.size}") Log.d(TAG, "Subscribing callback, service count: ${services.size}")
callbacks.add(listener) callbacks.add(listener)
listener.onServicesUpdated(services) listener.onServicesUpdated(services)
} }
} }
}
/** /**
* Removes a callback from this controller. * Removes a callback from this controller.

View File

@@ -796,24 +796,6 @@ class ControlsControllerImplTest : SysuiTestCase() {
.getCachedFavoritesAndRemoveFor(TEST_COMPONENT_2) .getCachedFavoritesAndRemoveFor(TEST_COMPONENT_2)
} }
@Test
fun testListingCallbackNotListeningWhileReadingFavorites() {
val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
}
val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
`when`(pendingResult.sendingUserId).thenReturn(otherUser)
broadcastReceiverCaptor.value.pendingResult = pendingResult
broadcastReceiverCaptor.value.onReceive(mContext, intent)
val inOrder = inOrder(persistenceWrapper, listingController)
inOrder.verify(listingController).removeCallback(listingCallbackCaptor.value)
inOrder.verify(persistenceWrapper).readFavorites()
inOrder.verify(listingController).addCallback(listingCallbackCaptor.value)
}
@Test @Test
fun testSeedFavoritesForComponent() { fun testSeedFavoritesForComponent() {
var succeeded = false var succeeded = false

View File

@@ -65,7 +65,10 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
@Mock @Mock
private lateinit var serviceInfo: ServiceInfo private lateinit var serviceInfo: ServiceInfo
@Mock @Mock
private lateinit var componentName: ComponentName private lateinit var serviceInfo2: ServiceInfo
private var componentName = ComponentName("pkg1", "class1")
private var componentName2 = ComponentName("pkg2", "class2")
private val executor = FakeExecutor(FakeSystemClock()) private val executor = FakeExecutor(FakeSystemClock())
@@ -82,6 +85,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
`when`(serviceInfo.componentName).thenReturn(componentName) `when`(serviceInfo.componentName).thenReturn(componentName)
`when`(serviceInfo2.componentName).thenReturn(componentName2)
val wrapper = object : ContextWrapper(mContext) { val wrapper = object : ContextWrapper(mContext) {
override fun createContextAsUser(user: UserHandle, flags: Int): Context { override fun createContextAsUser(user: UserHandle, flags: Int): Context {
@@ -179,7 +183,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
} }
@Test @Test
fun testChangeUserResetsExistingCallbackServices() { fun testChangeUserSendsCorrectServiceUpdate() {
val list = listOf(serviceInfo) val list = listOf(serviceInfo)
controller.addCallback(mockCallback) controller.addCallback(mockCallback)
@@ -197,10 +201,21 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
assertEquals(1, captor.value.size) assertEquals(1, captor.value.size)
reset(mockCallback) reset(mockCallback)
reset(mockSL)
val updatedList = listOf(serviceInfo)
serviceListingCallbackCaptor.value.onServicesReloaded(updatedList)
controller.changeUser(UserHandle.of(otherUser)) controller.changeUser(UserHandle.of(otherUser))
executor.runAllReady() executor.runAllReady()
assertEquals(otherUser, controller.currentUserId) assertEquals(otherUser, controller.currentUserId)
// this event should was triggered just before the user change, and should
// be ignored
verify(mockCallback, never()).onServicesUpdated(any())
serviceListingCallbackCaptor.value.onServicesReloaded(emptyList<ServiceInfo>())
executor.runAllReady()
verify(mockCallback).onServicesUpdated(capture(captor)) verify(mockCallback).onServicesUpdated(capture(captor))
assertEquals(0, captor.value.size) assertEquals(0, captor.value.size)
} }