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:
@@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager.GET_ACTIVITIES
|
import android.content.pm.PackageManager.GET_ACTIVITIES
|
||||||
import android.content.pm.PackageManager.PackageInfoFlags
|
import android.content.pm.PackageManager.PackageInfoFlags
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
@@ -56,26 +57,21 @@ class PictureInPictureListModel(private val context: Context) :
|
|||||||
private val packageManager = context.packageManager
|
private val packageManager = context.packageManager
|
||||||
|
|
||||||
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
|
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
|
||||||
userIdFlow.map(::getPictureInPicturePackages).combine(appListFlow) {
|
userIdFlow.map(::getPictureInPicturePackages)
|
||||||
pictureInPicturePackages,
|
.combine(appListFlow) { pictureInPicturePackages, appList ->
|
||||||
appList ->
|
appList.map { app ->
|
||||||
appList.map { app ->
|
createPictureInPictureRecord(
|
||||||
createPictureInPictureRecord(
|
app = app,
|
||||||
app = app,
|
isSupport = app.packageName in pictureInPicturePackages,
|
||||||
isSupport = app.packageName in pictureInPicturePackages,
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun transformItem(app: ApplicationInfo): PictureInPictureRecord {
|
override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord(
|
||||||
return createPictureInPictureRecord(
|
app = app,
|
||||||
app = app,
|
isSupport = app.installed &&
|
||||||
isSupport = app.installed &&
|
getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true,
|
||||||
packageManager
|
)
|
||||||
.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
|
|
||||||
.supportsPictureInPicture(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) =
|
private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) =
|
||||||
PictureInPictureRecord(
|
PictureInPictureRecord(
|
||||||
@@ -103,13 +99,36 @@ class PictureInPictureListModel(private val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getPictureInPicturePackages(userId: Int): Set<String> =
|
private fun getPictureInPicturePackages(userId: Int): Set<String> =
|
||||||
packageManager
|
getPackageAndActivityInfoList(userId)
|
||||||
.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId)
|
|
||||||
.filter { it.supportsPictureInPicture() }
|
.filter { it.supportsPictureInPicture() }
|
||||||
.map { it.packageName }
|
.map { it.packageName }
|
||||||
.toSet()
|
.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 {
|
companion object {
|
||||||
|
private const val TAG = "PictureInPictureListModel"
|
||||||
|
|
||||||
private fun PackageInfo.supportsPictureInPicture() =
|
private fun PackageInfo.supportsPictureInPicture() =
|
||||||
activities?.any(ActivityInfo::supportsPictureInPicture) ?: false
|
activities?.any(ActivityInfo::supportsPictureInPicture) ?: false
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.PackageInfoFlags
|
import android.content.pm.PackageManager.PackageInfoFlags
|
||||||
|
import android.os.DeadSystemRuntimeException
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
@@ -100,6 +101,23 @@ class PictureInPictureTest {
|
|||||||
assertThat(record.isSupport).isTrue()
|
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
|
@Test
|
||||||
fun transformItem() {
|
fun transformItem() {
|
||||||
whenever(
|
whenever(
|
||||||
@@ -114,6 +132,20 @@ class PictureInPictureTest {
|
|||||||
assertThat(record.isSupport).isTrue()
|
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
|
@Test
|
||||||
fun filter_isSupport() = runTest {
|
fun filter_isSupport() = runTest {
|
||||||
val record = createRecord(isSupport = true)
|
val record = createRecord(isSupport = true)
|
||||||
|
Reference in New Issue
Block a user