Fix crash of PictureInPicture

This follows change I3115cf1b99a305efef192a0dcf3e809eb7903d0a

PackageManager.getPackageInfoAsUser() will throw exceptions when the
package is too large which is a known issue to PackageManager but very
low priority given resourcing constraints. As per the PackageManager
team suggestion, catch the exception on the app side to alleviate the
impact to the PictureInPicture & App info page.

Fix: 283076353
Fix: 283354211
Test: Unit test
Change-Id: Iad2bf9fbfca6ee7f604fec1c4afa1b9382f6ec7e
This commit is contained in:
Chaohui Wang
2023-05-24 19:41:20 +08:00
parent 71a5715e3e
commit ce56dcc30b
2 changed files with 71 additions and 20 deletions

View File

@@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager.GET_ACTIVITIES
import android.content.pm.PackageManager.PackageInfoFlags
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import com.android.settings.R
@@ -56,9 +57,8 @@ class PictureInPictureListModel(private val context: Context) :
private val packageManager = context.packageManager
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
userIdFlow.map(::getPictureInPicturePackages).combine(appListFlow) {
pictureInPicturePackages,
appList ->
userIdFlow.map(::getPictureInPicturePackages)
.combine(appListFlow) { pictureInPicturePackages, appList ->
appList.map { app ->
createPictureInPictureRecord(
app = app,
@@ -67,15 +67,11 @@ class PictureInPictureListModel(private val context: Context) :
}
}
override fun transformItem(app: ApplicationInfo): PictureInPictureRecord {
return createPictureInPictureRecord(
override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord(
app = app,
isSupport = app.installed &&
packageManager
.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
.supportsPictureInPicture(),
getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true,
)
}
private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) =
PictureInPictureRecord(
@@ -103,13 +99,36 @@ class PictureInPictureListModel(private val context: Context) :
}
private fun getPictureInPicturePackages(userId: Int): Set<String> =
packageManager
.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId)
getPackageAndActivityInfoList(userId)
.filter { it.supportsPictureInPicture() }
.map { it.packageName }
.toSet()
private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try {
packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
} catch (e: Exception) {
// Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause
// exception sometimes. Since we reply on this flag to retrieve the Picture In Picture
// packages, we need to catch the exception to alleviate the impact before PackageManager
// fixing this issue or provide a better api.
Log.e(TAG, "Exception while getPackageInfoAsUser", e)
null
}
private fun getPackageAndActivityInfoList(userId: Int): List<PackageInfo> = try {
packageManager.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId)
} catch (e: Exception) {
// Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause
// exception sometimes. Since we reply on this flag to retrieve the Picture In Picture
// packages, we need to catch the exception to alleviate the impact before PackageManager
// fixing this issue or provide a better api.
Log.e(TAG, "Exception while getInstalledPackagesAsUser", e)
emptyList()
}
companion object {
private const val TAG = "PictureInPictureListModel"
private fun PackageInfo.supportsPictureInPicture() =
activities?.any(ActivityInfo::supportsPictureInPicture) ?: false

View File

@@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import android.os.DeadSystemRuntimeException
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
@@ -100,6 +101,23 @@ class PictureInPictureTest {
assertThat(record.isSupport).isTrue()
}
@Test
fun transform_getInstalledPackagesAsUserThrowsException_treatAsNotSupported() = runTest {
whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt()))
.thenThrow(DeadSystemRuntimeException())
val recordListFlow = listModel.transform(
userIdFlow = flowOf(USER_ID),
appListFlow = flowOf(listOf(PICTURE_IN_PICTURE_APP)),
)
val recordList = recordListFlow.first()
assertThat(recordList).hasSize(1)
val record = recordList[0]
assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP)
assertThat(record.isSupport).isFalse()
}
@Test
fun transformItem() {
whenever(
@@ -114,6 +132,20 @@ class PictureInPictureTest {
assertThat(record.isSupport).isTrue()
}
@Test
fun transformItem_getPackageInfoAsUserThrowsException_treatAsNotSupported() {
whenever(
packageManager.getPackageInfoAsUser(
eq(PICTURE_IN_PICTURE_PACKAGE_NAME), any<PackageInfoFlags>(), eq(USER_ID)
)
).thenThrow(DeadSystemRuntimeException())
val record = listModel.transformItem(PICTURE_IN_PICTURE_APP)
assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP)
assertThat(record.isSupport).isFalse()
}
@Test
fun filter_isSupport() = runTest {
val record = createRecord(isSupport = true)