[Partial screensharing] Fix crash when having a recents app available only in work profile

AppIconLoader in the recent app selector crashed when getting
information about an app that is available only in work profile.
This is because package manager gets activity information only
for the current user.
Replaced with using private package manager wrapper that allows
to get activity info for any user.

Bug: 256835220
Test: open app selector with work profile apps in the recents
Test: com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoaderTest
Change-Id: Ib608176a80e4a8f02452de3bc72f01b184ee4680
This commit is contained in:
Nick Chameyev
2022-11-02 13:55:02 +00:00
parent 71bf4aaf9e
commit df18b67209
3 changed files with 99 additions and 5 deletions

View File

@@ -19,6 +19,7 @@ package com.android.systemui.mediaprojection.appselector
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -91,6 +92,11 @@ interface MediaProjectionAppSelectorModule {
activity: MediaProjectionAppSelectorActivity
): ConfigurationController = ConfigurationControllerImpl(activity)
@Provides
fun bindIconFactory(
context: Context
): IconFactory = IconFactory.obtain(context)
@Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope

View File

@@ -19,13 +19,14 @@ package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.launcher3.icons.BaseIconFactory.IconOptions
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.system.PackageManagerWrapper
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -38,14 +39,18 @@ class IconLoaderLibAppIconLoader
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val context: Context,
private val packageManager: PackageManager
// Use wrapper to access hidden API that allows to get ActivityInfo for any user id
private val packageManagerWrapper: PackageManagerWrapper,
private val packageManager: PackageManager,
private val iconFactoryProvider: Provider<IconFactory>
) : AppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) {
IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
val activityInfo = packageManager
.getActivityInfo(component, ComponentInfoFlags.of(0))
iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
val activityInfo =
packageManagerWrapper.getActivityInfo(component, userId)
?: return@withContext null
val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
val userHandler = UserHandle.of(userId)
val options = IconOptions().apply { setUser(userHandler) }

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import androidx.test.filters.SmallTest
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.FastBitmapDrawable
import com.android.launcher3.icons.IconFactory
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.system.PackageManagerWrapper
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
class IconLoaderLibAppIconLoaderTest : SysuiTestCase() {
private val iconFactory: IconFactory = mock()
private val packageManagerWrapper: PackageManagerWrapper = mock()
private val packageManager: PackageManager = mock()
private val dispatcher = Dispatchers.Unconfined
private val appIconLoader =
IconLoaderLibAppIconLoader(
backgroundDispatcher = dispatcher,
context = context,
packageManagerWrapper = packageManagerWrapper,
packageManager = packageManager,
iconFactoryProvider = { iconFactory }
)
@Test
fun loadIcon_loadsIconUsingTheSameUserId() {
val icon = createIcon()
val component = ComponentName("com.test", "TestApplication")
givenIcon(component, userId = 123, icon = icon)
val loadedIcon = runBlocking { appIconLoader.loadIcon(userId = 123, component = component) }
assertThat(loadedIcon).isEqualTo(icon)
}
private fun givenIcon(component: ComponentName, userId: Int, icon: FastBitmapDrawable) {
val activityInfo = mock<ActivityInfo>()
whenever(packageManagerWrapper.getActivityInfo(component, userId)).thenReturn(activityInfo)
val rawIcon = mock<Drawable>()
whenever(activityInfo.loadIcon(packageManager)).thenReturn(rawIcon)
val bitmapInfo = mock<BitmapInfo>()
whenever(iconFactory.createBadgedIconBitmap(eq(rawIcon), any())).thenReturn(bitmapInfo)
whenever(bitmapInfo.newIcon(context)).thenReturn(icon)
}
private fun createIcon(): FastBitmapDrawable =
FastBitmapDrawable(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888))
}