diff options
| author | 2023-06-27 15:50:58 -0400 | |
|---|---|---|
| committer | 2023-06-28 08:49:12 -0400 | |
| commit | 00fe868a7446b6d160b1c66208c94abbe3783f49 (patch) | |
| tree | 2ffa897bf3049be2be9c02a5160882ee102397b7 | |
| parent | 2e366454dee947195d6e24fd150aa593ab39c2e0 (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
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( |