Merge "Fix crash of PictureInPicture" into udc-dev

This commit is contained in:
Chaohui Wang
2023-05-25 06:02:59 +00:00
committed by Android (Google) Code Review
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.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,9 +57,8 @@ 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,
@@ -67,15 +67,11 @@ class PictureInPictureListModel(private val context: Context) :
} }
} }
override fun transformItem(app: ApplicationInfo): PictureInPictureRecord { override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord(
return createPictureInPictureRecord(
app = app, app = app,
isSupport = app.installed && isSupport = app.installed &&
packageManager getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true,
.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

View File

@@ -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)