From 06c02d7832d92ac52056ffcaa4762e3a356a8e63 Mon Sep 17 00:00:00 2001 From: Evan Severson Date: Fri, 14 Apr 2023 11:19:22 -0700 Subject: [PATCH] Update FgsManager UI information on bg thread without lock ActivityManagerService blocks on an awaiting incoming call while at the same time requiring that the lock be held to provide information used to make UI decisions. Here we copy data about which tasks are running and who the current profiles are under then lock then post to the background thread to load any needed UI data before updating the dialog on the main thread. Test: FgsManagerControllerTest Bug: 276882028 Change-Id: I53664adbb534b05de8b130ed06fca0535f683156 --- .../systemui/qs/FgsManagerController.kt | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt index 0641eec154bbb..a3b901b675ed7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt @@ -44,6 +44,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.annotation.GuardedBy import androidx.annotation.VisibleForTesting +import androidx.annotation.WorkerThread import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -201,7 +202,7 @@ class FgsManagerControllerImpl @Inject constructor( @GuardedBy("lock") private val appListAdapter: AppListAdapter = AppListAdapter() - @GuardedBy("lock") + /* Only mutate on the background thread */ private var runningApps: ArrayMap = ArrayMap() private val userTrackerCallback = object : UserTracker.Callback { @@ -374,11 +375,6 @@ class FgsManagerControllerImpl @Inject constructor( override fun showDialog(expandable: Expandable?) { synchronized(lock) { if (dialog == null) { - - runningTaskIdentifiers.keys.forEach { - it.updateUiControl() - } - val dialog = SystemUIDialog(context) dialog.setTitle(R.string.fgs_manager_dialog_title) dialog.setMessage(R.string.fgs_manager_dialog_message) @@ -421,33 +417,53 @@ class FgsManagerControllerImpl @Inject constructor( } } - backgroundExecutor.execute { - synchronized(lock) { - updateAppItemsLocked() - } - } + updateAppItemsLocked(refreshUiControls = true) } } } @GuardedBy("lock") - private fun updateAppItemsLocked() { + private fun updateAppItemsLocked(refreshUiControls: Boolean = false) { if (dialog == null) { - runningApps.clear() + backgroundExecutor.execute { + clearRunningApps() + } return } - val addedPackages = runningTaskIdentifiers.keys.filter { - currentProfileIds.contains(it.userId) && + val packagesToStartTime = runningTaskIdentifiers.mapValues { it.value.startTime } + val profileIds = currentProfileIds.toSet() + backgroundExecutor.execute { + updateAppItems(packagesToStartTime, profileIds, refreshUiControls) + } + } + + /** + * Must be called on the background thread. + */ + @WorkerThread + private fun updateAppItems( + packages: Map, + profileIds: Set, + refreshUiControls: Boolean = true + ) { + if (refreshUiControls) { + packages.forEach { (pkg, _) -> + pkg.updateUiControl() + } + } + + val addedPackages = packages.keys.filter { + profileIds.contains(it.userId) && it.uiControl != UIControl.HIDE_ENTRY && runningApps[it]?.stopped != true } - val removedPackages = runningApps.keys.filter { !runningTaskIdentifiers.containsKey(it) } + val removedPackages = runningApps.keys.filter { it !in packages } addedPackages.forEach { val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId) runningApps[it] = RunningApp( it.userId, it.packageName, - runningTaskIdentifiers[it]!!.startTime, it.uiControl, + packages[it]!!, it.uiControl, packageManager.getApplicationLabel(ai), packageManager.getUserBadgedIcon( packageManager.getApplicationIcon(ai), UserHandle.of(it.userId) @@ -472,6 +488,14 @@ class FgsManagerControllerImpl @Inject constructor( } } + /** + * Must be called on the background thread. + */ + @WorkerThread + private fun clearRunningApps() { + runningApps.clear() + } + private fun stopPackage(userId: Int, packageName: String, timeStarted: Long) { logEvent(stopped = true, packageName, userId, timeStarted) val userPackageKey = UserPackage(userId, packageName)