summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Fabián Kozynski <kozynski@google.com> 2023-06-27 15:50:58 -0400
committer Fabián Kozynski <kozynski@google.com> 2023-06-28 08:49:12 -0400
commit00fe868a7446b6d160b1c66208c94abbe3783f49 (patch)
tree2ffa897bf3049be2be9c02a5160882ee102397b7
parent2e366454dee947195d6e24fd150aa593ab39c2e0 (diff)
Fix call to PackageManager exception
If PackageManager.getComponentEnabledSetting is called for a package that is not installed, it will throw an exception. This was caused because PackageManager.queryIntentServicesAsUser takes a user parameter, but getComponentEnabledSetting uses the userId from the context. Instead, make sure that the PackageManager used corresponds to the correct user. Fixes: 288951969 Test: atest InstalledTilesComponentRepositoryImplTest Test: manual: install TileService app in secondary user only and switch to that user Change-Id: I336df215fe70d58f1c5747a928529c5e9496f032
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt51
3 files changed, 77 insertions, 6 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
index 498f403e8c7a..6e7e09959697 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
@@ -51,12 +51,26 @@ class InstalledTilesComponentRepositoryImpl
@Inject
constructor(
@Application private val applicationContext: Context,
- private val packageManager: PackageManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : InstalledTilesComponentRepository {
- override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> =
- conflatedCallbackFlow {
+ override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> {
+ /*
+ * In order to query [PackageManager] for different users, this implementation will call
+ * [Context.createContextAsUser] and retrieve the [PackageManager] from that context.
+ */
+ val packageManager =
+ if (applicationContext.userId == userId) {
+ applicationContext.packageManager
+ } else {
+ applicationContext
+ .createContextAsUser(
+ UserHandle.of(userId),
+ /* flags */ 0,
+ )
+ .packageManager
+ }
+ return conflatedCallbackFlow {
val receiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@@ -74,12 +88,13 @@ constructor(
awaitClose { applicationContext.unregisterReceiver(receiver) }
}
.onStart { emit(Unit) }
- .map { reloadComponents(userId) }
+ .map { reloadComponents(userId, packageManager) }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ }
@WorkerThread
- private fun reloadComponents(userId: Int): Set<ComponentName> {
+ private fun reloadComponents(userId: Int, packageManager: PackageManager): Set<ComponentName> {
return packageManager
.queryIntentServicesAsUser(INTENT, FLAGS, userId)
.mapNotNull { it.serviceInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
index 891ee0cf66d7..e32ed5fd3671 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
@@ -16,12 +16,19 @@
package com.android.systemui.util.kotlin
+import android.annotation.UserHandleAware
import android.annotation.WorkerThread
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
import com.android.systemui.util.Assert
+/**
+ * Determines whether a component is actually enabled (not just its default value).
+ *
+ * @throws IllegalArgumentException if the component is not found
+ */
@WorkerThread
+@UserHandleAware
fun PackageManager.isComponentActuallyEnabled(componentInfo: ComponentInfo): Boolean {
Assert.isNotMainThread()
return when (getComponentEnabledSetting(componentInfo.componentName)) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 18f3837a7d36..dc0fae53f0c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -75,6 +76,9 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(context.createContextAsUser(any(), anyInt())).thenReturn(context)
+ whenever(context.packageManager).thenReturn(packageManager)
+
// Use the default value set in the ServiceInfo
whenever(packageManager.getComponentEnabledSetting(any()))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
@@ -86,7 +90,6 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
underTest =
InstalledTilesComponentRepositoryImpl(
context,
- packageManager,
testDispatcher,
)
}
@@ -224,6 +227,52 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
assertThat(componentNames).isEmpty()
}
+ @Test
+ fun packageOnlyInSecondaryUser_noException() =
+ testScope.runTest {
+ val userId = 10
+ val secondaryUserContext: Context = mock()
+ whenever(context.userId).thenReturn(0) // System context
+ whenever(context.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
+ .thenReturn(secondaryUserContext)
+
+ val secondaryUserPackageManager: PackageManager = mock()
+ whenever(secondaryUserContext.packageManager).thenReturn(secondaryUserPackageManager)
+
+ // Use the default value set in the ServiceInfo
+ whenever(secondaryUserPackageManager.getComponentEnabledSetting(any()))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+ // System User package manager throws exception if the component doesn't exist for that
+ // user
+ whenever(packageManager.getComponentEnabledSetting(TEST_COMPONENT))
+ .thenThrow(IllegalArgumentException()) // The package is not in the system user
+
+ val resolveInfo =
+ ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true)
+ // Both package manager should return the same (because the query is for the secondary
+ // user)
+ whenever(
+ secondaryUserPackageManager.queryIntentServicesAsUser(
+ matchIntent(),
+ matchFlags(),
+ eq(userId)
+ )
+ )
+ .thenReturn(listOf(resolveInfo))
+ whenever(
+ packageManager.queryIntentServicesAsUser(
+ matchIntent(),
+ matchFlags(),
+ eq(userId)
+ )
+ )
+ .thenReturn(listOf(resolveInfo))
+
+ val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+
+ assertThat(componentNames).containsExactly(TEST_COMPONENT)
+ }
+
private fun getRegisteredReceiver(): BroadcastReceiver {
verify(context)
.registerReceiverAsUser(