Loading core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +8 −1 Original line number Diff line number Diff line Loading @@ -302,7 +302,14 @@ public class ParsedActivityUtils { } String permission = array.getNonConfigurationString(permissionAttr, 0); if (isAlias) { // An alias will override permissions to allow referencing an Activity through its alias // without needing the original permission. If an alias needs the same permission, // it must be re-declared. activity.setPermission(permission); } else { activity.setPermission(permission != null ? permission : pkg.getPermission()); } final boolean setExported = array.hasValue(exportedAttr); if (setExported) { Loading core/java/android/content/pm/parsing/component/ParsedComponentUtils.java +20 −9 Original line number Diff line number Diff line Loading @@ -20,7 +20,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.ParsingUtils; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; Loading @@ -29,9 +32,6 @@ import android.text.TextUtils; import android.util.TypedValue; import com.android.internal.annotations.VisibleForTesting; import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; /** @hide */ class ParsedComponentUtils { Loading Loading @@ -60,16 +60,27 @@ class ParsedComponentUtils { component.setName(className); component.setPackageName(packageName); if (useRoundIcon) { component.icon = array.getResourceId(roundIconAttr, 0); int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0; if (roundIconVal != 0) { component.icon = roundIconVal; component.nonLocalizedLabel = null; } else { int iconVal = array.getResourceId(iconAttr, 0); if (iconVal != 0) { component.icon = iconVal; component.nonLocalizedLabel = null; } } if (component.icon == 0) { component.icon = array.getResourceId(iconAttr, 0); int logoVal = array.getResourceId(logoAttr, 0); if (logoVal != 0) { component.logo = logoVal; } component.logo = array.getResourceId(logoAttr, 0); component.banner = array.getResourceId(bannerAttr, 0); int bannerVal = array.getResourceId(bannerAttr, 0); if (bannerVal != 0) { component.banner = bannerVal; } if (descriptionAttr != null) { component.descriptionRes = array.getResourceId(descriptionAttr, 0); Loading services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt +78 −7 Original line number Diff line number Diff line Loading @@ -18,8 +18,8 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager import android.platform.test.annotations.Presubmit import androidx.test.filters.LargeTest import com.google.common.truth.Expect import com.google.common.truth.Truth.assertWithMessage import org.junit.Rule import org.junit.Test Loading Loading @@ -52,6 +52,7 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { } } @LargeTest @Test fun packageInfoEquality() { val flags = PackageManager.GET_ACTIVITIES or Loading @@ -65,7 +66,9 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { PackageManager.GET_SERVICES or PackageManager.GET_SHARED_LIBRARY_FILES or PackageManager.GET_SIGNATURES or PackageManager.GET_SIGNING_CERTIFICATES PackageManager.GET_SIGNING_CERTIFICATES or PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE val oldPackageInfo = oldPackages.asSequence().map { oldPackageInfo(it, flags) } val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) } Loading @@ -77,11 +80,79 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { } else { "$firstName | $secondName" } expect.withMessage("${it.first?.applicationInfo?.sourceDir} $packageName") .that(it.first?.dumpToString()) .isEqualTo(it.second?.dumpToString()) // Main components are asserted independently to separate the failures. Otherwise the // comparison would include every component in one massive string. val prefix = "${it.first?.applicationInfo?.sourceDir} $packageName" expect.withMessage("$prefix PackageInfo") .that(it.second?.dumpToString()) .isEqualTo(it.first?.dumpToString()) expect.withMessage("$prefix ApplicationInfo") .that(it.second?.applicationInfo?.dumpToString()) .isEqualTo(it.first?.applicationInfo?.dumpToString()) val firstActivityNames = it.first?.activities?.map { it.name } ?: emptyList() val secondActivityNames = it.second?.activities?.map { it.name } ?: emptyList() expect.withMessage("$prefix activities") .that(secondActivityNames) .containsExactlyElementsIn(firstActivityNames) .inOrder() if (!it.first?.activities.isNullOrEmpty() && !it.second?.activities.isNullOrEmpty()) { it.first?.activities?.zip(it.second?.activities!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } val firstReceiverNames = it.first?.receivers?.map { it.name } ?: emptyList() val secondReceiverNames = it.second?.receivers?.map { it.name } ?: emptyList() expect.withMessage("$prefix receivers") .that(secondReceiverNames) .containsExactlyElementsIn(firstReceiverNames) .inOrder() if (!it.first?.receivers.isNullOrEmpty() && !it.second?.receivers.isNullOrEmpty()) { it.first?.receivers?.zip(it.second?.receivers!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } val firstProviderNames = it.first?.providers?.map { it.name } ?: emptyList() val secondProviderNames = it.second?.providers?.map { it.name } ?: emptyList() expect.withMessage("$prefix providers") .that(secondProviderNames) .containsExactlyElementsIn(firstProviderNames) .inOrder() if (!it.first?.providers.isNullOrEmpty() && !it.second?.providers.isNullOrEmpty()) { it.first?.providers?.zip(it.second?.providers!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } val firstServiceNames = it.first?.services?.map { it.name } ?: emptyList() val secondServiceNames = it.second?.services?.map { it.name } ?: emptyList() expect.withMessage("$prefix services") .that(secondServiceNames) .containsExactlyElementsIn(firstServiceNames) .inOrder() if (!it.first?.services.isNullOrEmpty() && !it.second?.services.isNullOrEmpty()) { it.first?.services?.zip(it.second?.services!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } } } } services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt +137 −37 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.pm.parsing import android.content.Context import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo import android.content.pm.ComponentInfo import android.content.pm.ConfigurationInfo import android.content.pm.FeatureInfo import android.content.pm.InstrumentationInfo Loading @@ -27,6 +28,8 @@ import android.content.pm.PackageParser import android.content.pm.PackageUserState import android.content.pm.PermissionInfo import android.content.pm.ProviderInfo import android.content.pm.ServiceInfo import android.os.Bundle import android.os.Debug import android.os.Environment import android.util.SparseArray Loading @@ -38,8 +41,10 @@ import com.android.server.pm.pkg.PackageStateUnserialized import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import org.junit.BeforeClass import org.mockito.Mockito import org.mockito.Mockito.any import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyInt import org.mockito.Mockito.anyString import org.mockito.Mockito.mock import java.io.File Loading @@ -47,7 +52,7 @@ open class AndroidPackageParsingTestBase { companion object { private const val VERIFY_ALL_APKS = false private const val VERIFY_ALL_APKS = true /** For auditing memory usage differences */ private const val DUMP_HPROF_TO_EXTERNAL = false Loading Loading @@ -81,10 +86,14 @@ open class AndroidPackageParsingTestBase { .filter { file -> file.name.endsWith(".apk") } .toList() } .distinct() private val dummyUserState = mock(PackageUserState::class.java).apply { installed = true Mockito.`when`(isAvailable(anyInt())).thenReturn(true) whenever(isAvailable(anyInt())) { true } whenever(isMatch(any<ComponentInfo>(), anyInt())) { true } whenever(isMatch(anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyString(), anyInt())) { true } } lateinit var oldPackages: List<PackageParser.Package> Loading Loading @@ -145,6 +154,7 @@ open class AndroidPackageParsingTestBase { private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> { this.pkg = aPkg whenever(pkgState) { PackageStateUnserialized() } whenever(readUserState(anyInt())) { dummyUserState } } } Loading @@ -156,19 +166,10 @@ open class AndroidPackageParsingTestBase { // The following methods prepend "this." because @hide APIs can cause an IDE to auto-import // the R.attr constant instead of referencing the field in an attempt to fix the error. /** * Known exclusions: * - [ApplicationInfo.credentialProtectedDataDir] * - [ApplicationInfo.dataDir] * - [ApplicationInfo.deviceProtectedDataDir] * - [ApplicationInfo.processName] * - [ApplicationInfo.publicSourceDir] * - [ApplicationInfo.scanPublicSourceDir] * - [ApplicationInfo.scanSourceDir] * - [ApplicationInfo.sourceDir] * These attributes used to be assigned post-package-parsing as part of another component, * but are now adjusted directly inside [PackageImpl]. */ // It's difficult to comment out a line in a triple quoted string, so this is used instead // to ignore specific fields. A comment is required to explain why a field was ignored. private fun Any?.ignored(comment: String): String = "IGNORED" protected fun ApplicationInfo.dumpToString() = """ appComponentFactory=${this.appComponentFactory} backupAgentName=${this.backupAgentName} Loading @@ -179,22 +180,31 @@ open class AndroidPackageParsingTestBase { compatibleWidthLimitDp=${this.compatibleWidthLimitDp} compileSdkVersion=${this.compileSdkVersion} compileSdkVersionCodename=${this.compileSdkVersionCodename} credentialProtectedDataDir=${this.credentialProtectedDataDir .ignored("Deferred pre-R, but assigned immediately in R")} crossProfile=${this.crossProfile.ignored("Added in R")} dataDir=${this.dataDir.ignored("Deferred pre-R, but assigned immediately in R")} descriptionRes=${this.descriptionRes} deviceProtectedDataDir=${this.deviceProtectedDataDir .ignored("Deferred pre-R, but assigned immediately in R")} enabled=${this.enabled} enabledSetting=${this.enabledSetting} flags=${Integer.toBinaryString(this.flags)} fullBackupContent=${this.fullBackupContent} gwpAsanMode=${this.gwpAsanMode.ignored("Added in R")} hiddenUntilInstalled=${this.hiddenUntilInstalled} icon=${this.icon} iconRes=${this.iconRes} installLocation=${this.installLocation} labelRes=${this.labelRes} largestWidthLimitDp=${this.largestWidthLimitDp} logo=${this.logo} longVersionCode=${this.longVersionCode} ${"".ignored("mHiddenApiPolicy is a private field")} manageSpaceActivityName=${this.manageSpaceActivityName} maxAspectRatio.compareTo(that.maxAspectRatio)=${this.maxAspectRatio} metaData=${this.metaData} minAspectRatio.compareTo(that.minAspectRatio)=${this.minAspectRatio} maxAspectRatio=${this.maxAspectRatio} metaData=${this.metaData.dumpToString()} minAspectRatio=${this.minAspectRatio} minSdkVersion=${this.minSdkVersion} name=${this.name} nativeLibraryDir=${this.nativeLibraryDir} Loading @@ -206,18 +216,27 @@ open class AndroidPackageParsingTestBase { permission=${this.permission} primaryCpuAbi=${this.primaryCpuAbi} privateFlags=${Integer.toBinaryString(this.privateFlags)} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} publicSourceDir=${this.publicSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} requiresSmallestWidthDp=${this.requiresSmallestWidthDp} resourceDirs=${this.resourceDirs?.contentToString()} roundIconRes=${this.roundIconRes} secondaryCpuAbi=${this.secondaryCpuAbi} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} scanPublicSourceDir=${this.scanPublicSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} scanSourceDir=${this.scanSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} seInfo=${this.seInfo} seInfoUser=${this.seInfoUser} secondaryCpuAbi=${this.secondaryCpuAbi} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()} sharedLibraryInfos=${this.sharedLibraryInfos} showUserIcon=${this.showUserIcon} sourceDir=${this.sourceDir .ignored("Deferred pre-R, but assigned immediately in R")} splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()} splitDependencies=${this.splitDependencies} splitDependencies=${this.splitDependencies.dumpToString()} splitNames=${this.splitNames?.contentToString()} splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()} splitSourceDirs=${this.splitSourceDirs?.contentToString()} Loading @@ -226,8 +245,8 @@ open class AndroidPackageParsingTestBase { targetSdkVersion=${this.targetSdkVersion} taskAffinity=${this.taskAffinity} theme=${this.theme} uid=${this.uid} uiOptions=${this.uiOptions} uid=${this.uid} versionCode=${this.versionCode} volumeUuid=${this.volumeUuid} zygotePreloadName=${this.zygotePreloadName} Loading @@ -241,19 +260,27 @@ open class AndroidPackageParsingTestBase { """.trimIndent() protected fun InstrumentationInfo.dumpToString() = """ banner=${this.banner} credentialProtectedDataDir=${this.credentialProtectedDataDir} dataDir=${this.dataDir} deviceProtectedDataDir=${this.deviceProtectedDataDir} functionalTest=${this.functionalTest} handleProfiling=${this.handleProfiling} icon=${this.icon} labelRes=${this.labelRes} logo=${this.logo} metaData=${this.metaData} name=${this.name} nativeLibraryDir=${this.nativeLibraryDir} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} primaryCpuAbi=${this.primaryCpuAbi} publicSourceDir=${this.publicSourceDir} secondaryCpuAbi=${this.secondaryCpuAbi} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} showUserIcon=${this.showUserIcon} sourceDir=${this.sourceDir} splitDependencies=${this.splitDependencies.sequence() .map { it.first to it.second?.contentToString() }.joinToString()} splitDependencies=${this.splitDependencies.dumpToString()} splitNames=${this.splitNames?.contentToString()} splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()} splitSourceDirs=${this.splitSourceDirs?.contentToString()} Loading @@ -262,25 +289,40 @@ open class AndroidPackageParsingTestBase { """.trimIndent() protected fun ActivityInfo.dumpToString() = """ banner=${this.banner} colorMode=${this.colorMode} configChanges=${this.configChanges} descriptionRes=${this.descriptionRes} directBootAware=${this.directBootAware} documentLaunchMode=${this.documentLaunchMode} enabled=${this.enabled} exported=${this.exported} flags=${Integer.toBinaryString(this.flags)} icon=${this.icon} labelRes=${this.labelRes} launchMode=${this.launchMode} launchToken=${this.launchToken} lockTaskLaunchMode=${this.lockTaskLaunchMode} logo=${this.logo} maxAspectRatio=${this.maxAspectRatio} maxRecents=${this.maxRecents} metaData=${this.metaData.dumpToString()} minAspectRatio=${this.minAspectRatio} name=${this.name} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} parentActivityName=${this.parentActivityName} permission=${this.permission} persistableMode=${this.persistableMode} privateFlags=${Integer.toBinaryString(this.privateFlags)} persistableMode=${this.persistableMode.ignored("Could be dropped pre-R, fixed in R")} privateFlags=${this.privateFlags} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} requestedVrComponent=${this.requestedVrComponent} resizeMode=${this.resizeMode} rotationAnimation=${this.rotationAnimation} screenOrientation=${this.screenOrientation} showUserIcon=${this.showUserIcon} softInputMode=${this.softInputMode} splitName=${this.splitName} targetActivity=${this.targetActivity} taskAffinity=${this.taskAffinity} theme=${this.theme} Loading @@ -300,30 +342,77 @@ open class AndroidPackageParsingTestBase { protected fun PermissionInfo.dumpToString() = """ backgroundPermission=${this.backgroundPermission} banner=${this.banner} descriptionRes=${this.descriptionRes} flags=${Integer.toBinaryString(this.flags)} group=${this.group} icon=${this.icon} labelRes=${this.labelRes} logo=${this.logo} metaData=${this.metaData.dumpToString()} name=${this.name} nonLocalizedDescription=${this.nonLocalizedDescription} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} protectionLevel=${this.protectionLevel} requestRes=${this.requestRes} showUserIcon=${this.showUserIcon} """.trimIndent() protected fun ProviderInfo.dumpToString() = """ applicationInfo=${this.applicationInfo.ignored("Already checked")} authority=${this.authority} banner=${this.banner} descriptionRes=${this.descriptionRes} directBootAware=${this.directBootAware} enabled=${this.enabled} exported=${this.exported} flags=${Integer.toBinaryString(this.flags)} forceUriPermissions=${this.forceUriPermissions} grantUriPermissions=${this.grantUriPermissions} icon=${this.icon} initOrder=${this.initOrder} isSyncable=${this.isSyncable} labelRes=${this.labelRes} logo=${this.logo} metaData=${this.metaData.dumpToString()} multiprocess=${this.multiprocess} name=${this.name} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} pathPermissions=${this.pathPermissions?.joinToString { "readPermission=${it.readPermission}\nwritePermission=${it.writePermission}" }} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} readPermission=${this.readPermission} showUserIcon=${this.showUserIcon} splitName=${this.splitName} uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()} writePermission=${this.writePermission} """.trimIndent() protected fun ServiceInfo.dumpToString() = """ applicationInfo=${this.applicationInfo.ignored("Already checked")} banner=${this.banner} descriptionRes=${this.descriptionRes} directBootAware=${this.directBootAware} enabled=${this.enabled} exported=${this.exported} flags=${Integer.toBinaryString(this.flags)} icon=${this.icon} labelRes=${this.labelRes} logo=${this.logo} mForegroundServiceType"${this.mForegroundServiceType} metaData=${this.metaData.dumpToString()} name=${this.name} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} permission=${this.permission} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} showUserIcon=${this.showUserIcon} splitName=${this.splitName} """.trimIndent() protected fun ConfigurationInfo.dumpToString() = """ reqGlEsVersion=${this.reqGlEsVersion} reqInputFeatures=${this.reqInputFeatures} Loading @@ -333,8 +422,10 @@ open class AndroidPackageParsingTestBase { """.trimIndent() protected fun PackageInfo.dumpToString() = """ activities=${this.activities?.joinToString { it.dumpToString() }} applicationInfo=${this.applicationInfo.dumpToString()} activities=${this.activities?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} applicationInfo=${this.applicationInfo.dumpToString() .ignored("Checked separately in test")} baseRevisionCode=${this.baseRevisionCode} compileSdkVersion=${this.compileSdkVersion} compileSdkVersionCodename=${this.compileSdkVersionCodename} Loading @@ -356,15 +447,18 @@ open class AndroidPackageParsingTestBase { overlayTarget=${this.overlayTarget} packageName=${this.packageName} permissions=${this.permissions?.joinToString { it.dumpToString() }} providers=${this.providers?.joinToString { it.dumpToString() }} receivers=${this.receivers?.joinToString { it.dumpToString() }} providers=${this.providers?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} receivers=${this.receivers?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }} requestedPermissions=${this.requestedPermissions?.contentToString()} requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()} requiredAccountType=${this.requiredAccountType} requiredForAllUsers=${this.requiredForAllUsers} restrictedAccountType=${this.restrictedAccountType} services=${this.services?.contentToString()} services=${this.services?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} sharedUserId=${this.sharedUserId} sharedUserLabel=${this.sharedUserLabel} signatures=${this.signatures?.joinToString { it.toCharsString() }} Loading @@ -378,11 +472,17 @@ open class AndroidPackageParsingTestBase { versionName=${this.versionName} """.trimIndent() @Suppress("unused") private fun <T> SparseArray<T>.sequence(): Sequence<Pair<Int, T>> { var index = 0 return generateSequence { index++.takeIf { it < size() }?.let { keyAt(it) to valueAt(index) } private fun Bundle?.dumpToString() = this?.keySet()?.associateWith { get(it) }?.toString() private fun <T> SparseArray<T>?.dumpToString(): String { if (this == null) { return "EMPTY" } val list = mutableListOf<Pair<Int, T>>() for (index in (0 until size())) { list += keyAt(index) to valueAt(index) } return list.toString() } } Loading
core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +8 −1 Original line number Diff line number Diff line Loading @@ -302,7 +302,14 @@ public class ParsedActivityUtils { } String permission = array.getNonConfigurationString(permissionAttr, 0); if (isAlias) { // An alias will override permissions to allow referencing an Activity through its alias // without needing the original permission. If an alias needs the same permission, // it must be re-declared. activity.setPermission(permission); } else { activity.setPermission(permission != null ? permission : pkg.getPermission()); } final boolean setExported = array.hasValue(exportedAttr); if (setExported) { Loading
core/java/android/content/pm/parsing/component/ParsedComponentUtils.java +20 −9 Original line number Diff line number Diff line Loading @@ -20,7 +20,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.ParsingUtils; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; Loading @@ -29,9 +32,6 @@ import android.text.TextUtils; import android.util.TypedValue; import com.android.internal.annotations.VisibleForTesting; import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; /** @hide */ class ParsedComponentUtils { Loading Loading @@ -60,16 +60,27 @@ class ParsedComponentUtils { component.setName(className); component.setPackageName(packageName); if (useRoundIcon) { component.icon = array.getResourceId(roundIconAttr, 0); int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0; if (roundIconVal != 0) { component.icon = roundIconVal; component.nonLocalizedLabel = null; } else { int iconVal = array.getResourceId(iconAttr, 0); if (iconVal != 0) { component.icon = iconVal; component.nonLocalizedLabel = null; } } if (component.icon == 0) { component.icon = array.getResourceId(iconAttr, 0); int logoVal = array.getResourceId(logoAttr, 0); if (logoVal != 0) { component.logo = logoVal; } component.logo = array.getResourceId(logoAttr, 0); component.banner = array.getResourceId(bannerAttr, 0); int bannerVal = array.getResourceId(bannerAttr, 0); if (bannerVal != 0) { component.banner = bannerVal; } if (descriptionAttr != null) { component.descriptionRes = array.getResourceId(descriptionAttr, 0); Loading
services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt +78 −7 Original line number Diff line number Diff line Loading @@ -18,8 +18,8 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager import android.platform.test.annotations.Presubmit import androidx.test.filters.LargeTest import com.google.common.truth.Expect import com.google.common.truth.Truth.assertWithMessage import org.junit.Rule import org.junit.Test Loading Loading @@ -52,6 +52,7 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { } } @LargeTest @Test fun packageInfoEquality() { val flags = PackageManager.GET_ACTIVITIES or Loading @@ -65,7 +66,9 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { PackageManager.GET_SERVICES or PackageManager.GET_SHARED_LIBRARY_FILES or PackageManager.GET_SIGNATURES or PackageManager.GET_SIGNING_CERTIFICATES PackageManager.GET_SIGNING_CERTIFICATES or PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE val oldPackageInfo = oldPackages.asSequence().map { oldPackageInfo(it, flags) } val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) } Loading @@ -77,11 +80,79 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { } else { "$firstName | $secondName" } expect.withMessage("${it.first?.applicationInfo?.sourceDir} $packageName") .that(it.first?.dumpToString()) .isEqualTo(it.second?.dumpToString()) // Main components are asserted independently to separate the failures. Otherwise the // comparison would include every component in one massive string. val prefix = "${it.first?.applicationInfo?.sourceDir} $packageName" expect.withMessage("$prefix PackageInfo") .that(it.second?.dumpToString()) .isEqualTo(it.first?.dumpToString()) expect.withMessage("$prefix ApplicationInfo") .that(it.second?.applicationInfo?.dumpToString()) .isEqualTo(it.first?.applicationInfo?.dumpToString()) val firstActivityNames = it.first?.activities?.map { it.name } ?: emptyList() val secondActivityNames = it.second?.activities?.map { it.name } ?: emptyList() expect.withMessage("$prefix activities") .that(secondActivityNames) .containsExactlyElementsIn(firstActivityNames) .inOrder() if (!it.first?.activities.isNullOrEmpty() && !it.second?.activities.isNullOrEmpty()) { it.first?.activities?.zip(it.second?.activities!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } val firstReceiverNames = it.first?.receivers?.map { it.name } ?: emptyList() val secondReceiverNames = it.second?.receivers?.map { it.name } ?: emptyList() expect.withMessage("$prefix receivers") .that(secondReceiverNames) .containsExactlyElementsIn(firstReceiverNames) .inOrder() if (!it.first?.receivers.isNullOrEmpty() && !it.second?.receivers.isNullOrEmpty()) { it.first?.receivers?.zip(it.second?.receivers!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } val firstProviderNames = it.first?.providers?.map { it.name } ?: emptyList() val secondProviderNames = it.second?.providers?.map { it.name } ?: emptyList() expect.withMessage("$prefix providers") .that(secondProviderNames) .containsExactlyElementsIn(firstProviderNames) .inOrder() if (!it.first?.providers.isNullOrEmpty() && !it.second?.providers.isNullOrEmpty()) { it.first?.providers?.zip(it.second?.providers!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } val firstServiceNames = it.first?.services?.map { it.name } ?: emptyList() val secondServiceNames = it.second?.services?.map { it.name } ?: emptyList() expect.withMessage("$prefix services") .that(secondServiceNames) .containsExactlyElementsIn(firstServiceNames) .inOrder() if (!it.first?.services.isNullOrEmpty() && !it.second?.services.isNullOrEmpty()) { it.first?.services?.zip(it.second?.services!!)?.forEach { expect.withMessage("$prefix ${it.first.name}") .that(it.second.dumpToString()) .isEqualTo(it.first.dumpToString()) } } } } }
services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt +137 −37 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.pm.parsing import android.content.Context import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo import android.content.pm.ComponentInfo import android.content.pm.ConfigurationInfo import android.content.pm.FeatureInfo import android.content.pm.InstrumentationInfo Loading @@ -27,6 +28,8 @@ import android.content.pm.PackageParser import android.content.pm.PackageUserState import android.content.pm.PermissionInfo import android.content.pm.ProviderInfo import android.content.pm.ServiceInfo import android.os.Bundle import android.os.Debug import android.os.Environment import android.util.SparseArray Loading @@ -38,8 +41,10 @@ import com.android.server.pm.pkg.PackageStateUnserialized import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import org.junit.BeforeClass import org.mockito.Mockito import org.mockito.Mockito.any import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyInt import org.mockito.Mockito.anyString import org.mockito.Mockito.mock import java.io.File Loading @@ -47,7 +52,7 @@ open class AndroidPackageParsingTestBase { companion object { private const val VERIFY_ALL_APKS = false private const val VERIFY_ALL_APKS = true /** For auditing memory usage differences */ private const val DUMP_HPROF_TO_EXTERNAL = false Loading Loading @@ -81,10 +86,14 @@ open class AndroidPackageParsingTestBase { .filter { file -> file.name.endsWith(".apk") } .toList() } .distinct() private val dummyUserState = mock(PackageUserState::class.java).apply { installed = true Mockito.`when`(isAvailable(anyInt())).thenReturn(true) whenever(isAvailable(anyInt())) { true } whenever(isMatch(any<ComponentInfo>(), anyInt())) { true } whenever(isMatch(anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyString(), anyInt())) { true } } lateinit var oldPackages: List<PackageParser.Package> Loading Loading @@ -145,6 +154,7 @@ open class AndroidPackageParsingTestBase { private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> { this.pkg = aPkg whenever(pkgState) { PackageStateUnserialized() } whenever(readUserState(anyInt())) { dummyUserState } } } Loading @@ -156,19 +166,10 @@ open class AndroidPackageParsingTestBase { // The following methods prepend "this." because @hide APIs can cause an IDE to auto-import // the R.attr constant instead of referencing the field in an attempt to fix the error. /** * Known exclusions: * - [ApplicationInfo.credentialProtectedDataDir] * - [ApplicationInfo.dataDir] * - [ApplicationInfo.deviceProtectedDataDir] * - [ApplicationInfo.processName] * - [ApplicationInfo.publicSourceDir] * - [ApplicationInfo.scanPublicSourceDir] * - [ApplicationInfo.scanSourceDir] * - [ApplicationInfo.sourceDir] * These attributes used to be assigned post-package-parsing as part of another component, * but are now adjusted directly inside [PackageImpl]. */ // It's difficult to comment out a line in a triple quoted string, so this is used instead // to ignore specific fields. A comment is required to explain why a field was ignored. private fun Any?.ignored(comment: String): String = "IGNORED" protected fun ApplicationInfo.dumpToString() = """ appComponentFactory=${this.appComponentFactory} backupAgentName=${this.backupAgentName} Loading @@ -179,22 +180,31 @@ open class AndroidPackageParsingTestBase { compatibleWidthLimitDp=${this.compatibleWidthLimitDp} compileSdkVersion=${this.compileSdkVersion} compileSdkVersionCodename=${this.compileSdkVersionCodename} credentialProtectedDataDir=${this.credentialProtectedDataDir .ignored("Deferred pre-R, but assigned immediately in R")} crossProfile=${this.crossProfile.ignored("Added in R")} dataDir=${this.dataDir.ignored("Deferred pre-R, but assigned immediately in R")} descriptionRes=${this.descriptionRes} deviceProtectedDataDir=${this.deviceProtectedDataDir .ignored("Deferred pre-R, but assigned immediately in R")} enabled=${this.enabled} enabledSetting=${this.enabledSetting} flags=${Integer.toBinaryString(this.flags)} fullBackupContent=${this.fullBackupContent} gwpAsanMode=${this.gwpAsanMode.ignored("Added in R")} hiddenUntilInstalled=${this.hiddenUntilInstalled} icon=${this.icon} iconRes=${this.iconRes} installLocation=${this.installLocation} labelRes=${this.labelRes} largestWidthLimitDp=${this.largestWidthLimitDp} logo=${this.logo} longVersionCode=${this.longVersionCode} ${"".ignored("mHiddenApiPolicy is a private field")} manageSpaceActivityName=${this.manageSpaceActivityName} maxAspectRatio.compareTo(that.maxAspectRatio)=${this.maxAspectRatio} metaData=${this.metaData} minAspectRatio.compareTo(that.minAspectRatio)=${this.minAspectRatio} maxAspectRatio=${this.maxAspectRatio} metaData=${this.metaData.dumpToString()} minAspectRatio=${this.minAspectRatio} minSdkVersion=${this.minSdkVersion} name=${this.name} nativeLibraryDir=${this.nativeLibraryDir} Loading @@ -206,18 +216,27 @@ open class AndroidPackageParsingTestBase { permission=${this.permission} primaryCpuAbi=${this.primaryCpuAbi} privateFlags=${Integer.toBinaryString(this.privateFlags)} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} publicSourceDir=${this.publicSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} requiresSmallestWidthDp=${this.requiresSmallestWidthDp} resourceDirs=${this.resourceDirs?.contentToString()} roundIconRes=${this.roundIconRes} secondaryCpuAbi=${this.secondaryCpuAbi} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} scanPublicSourceDir=${this.scanPublicSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} scanSourceDir=${this.scanSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} seInfo=${this.seInfo} seInfoUser=${this.seInfoUser} secondaryCpuAbi=${this.secondaryCpuAbi} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()} sharedLibraryInfos=${this.sharedLibraryInfos} showUserIcon=${this.showUserIcon} sourceDir=${this.sourceDir .ignored("Deferred pre-R, but assigned immediately in R")} splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()} splitDependencies=${this.splitDependencies} splitDependencies=${this.splitDependencies.dumpToString()} splitNames=${this.splitNames?.contentToString()} splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()} splitSourceDirs=${this.splitSourceDirs?.contentToString()} Loading @@ -226,8 +245,8 @@ open class AndroidPackageParsingTestBase { targetSdkVersion=${this.targetSdkVersion} taskAffinity=${this.taskAffinity} theme=${this.theme} uid=${this.uid} uiOptions=${this.uiOptions} uid=${this.uid} versionCode=${this.versionCode} volumeUuid=${this.volumeUuid} zygotePreloadName=${this.zygotePreloadName} Loading @@ -241,19 +260,27 @@ open class AndroidPackageParsingTestBase { """.trimIndent() protected fun InstrumentationInfo.dumpToString() = """ banner=${this.banner} credentialProtectedDataDir=${this.credentialProtectedDataDir} dataDir=${this.dataDir} deviceProtectedDataDir=${this.deviceProtectedDataDir} functionalTest=${this.functionalTest} handleProfiling=${this.handleProfiling} icon=${this.icon} labelRes=${this.labelRes} logo=${this.logo} metaData=${this.metaData} name=${this.name} nativeLibraryDir=${this.nativeLibraryDir} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} primaryCpuAbi=${this.primaryCpuAbi} publicSourceDir=${this.publicSourceDir} secondaryCpuAbi=${this.secondaryCpuAbi} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} showUserIcon=${this.showUserIcon} sourceDir=${this.sourceDir} splitDependencies=${this.splitDependencies.sequence() .map { it.first to it.second?.contentToString() }.joinToString()} splitDependencies=${this.splitDependencies.dumpToString()} splitNames=${this.splitNames?.contentToString()} splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()} splitSourceDirs=${this.splitSourceDirs?.contentToString()} Loading @@ -262,25 +289,40 @@ open class AndroidPackageParsingTestBase { """.trimIndent() protected fun ActivityInfo.dumpToString() = """ banner=${this.banner} colorMode=${this.colorMode} configChanges=${this.configChanges} descriptionRes=${this.descriptionRes} directBootAware=${this.directBootAware} documentLaunchMode=${this.documentLaunchMode} enabled=${this.enabled} exported=${this.exported} flags=${Integer.toBinaryString(this.flags)} icon=${this.icon} labelRes=${this.labelRes} launchMode=${this.launchMode} launchToken=${this.launchToken} lockTaskLaunchMode=${this.lockTaskLaunchMode} logo=${this.logo} maxAspectRatio=${this.maxAspectRatio} maxRecents=${this.maxRecents} metaData=${this.metaData.dumpToString()} minAspectRatio=${this.minAspectRatio} name=${this.name} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} parentActivityName=${this.parentActivityName} permission=${this.permission} persistableMode=${this.persistableMode} privateFlags=${Integer.toBinaryString(this.privateFlags)} persistableMode=${this.persistableMode.ignored("Could be dropped pre-R, fixed in R")} privateFlags=${this.privateFlags} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} requestedVrComponent=${this.requestedVrComponent} resizeMode=${this.resizeMode} rotationAnimation=${this.rotationAnimation} screenOrientation=${this.screenOrientation} showUserIcon=${this.showUserIcon} softInputMode=${this.softInputMode} splitName=${this.splitName} targetActivity=${this.targetActivity} taskAffinity=${this.taskAffinity} theme=${this.theme} Loading @@ -300,30 +342,77 @@ open class AndroidPackageParsingTestBase { protected fun PermissionInfo.dumpToString() = """ backgroundPermission=${this.backgroundPermission} banner=${this.banner} descriptionRes=${this.descriptionRes} flags=${Integer.toBinaryString(this.flags)} group=${this.group} icon=${this.icon} labelRes=${this.labelRes} logo=${this.logo} metaData=${this.metaData.dumpToString()} name=${this.name} nonLocalizedDescription=${this.nonLocalizedDescription} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} protectionLevel=${this.protectionLevel} requestRes=${this.requestRes} showUserIcon=${this.showUserIcon} """.trimIndent() protected fun ProviderInfo.dumpToString() = """ applicationInfo=${this.applicationInfo.ignored("Already checked")} authority=${this.authority} banner=${this.banner} descriptionRes=${this.descriptionRes} directBootAware=${this.directBootAware} enabled=${this.enabled} exported=${this.exported} flags=${Integer.toBinaryString(this.flags)} forceUriPermissions=${this.forceUriPermissions} grantUriPermissions=${this.grantUriPermissions} icon=${this.icon} initOrder=${this.initOrder} isSyncable=${this.isSyncable} labelRes=${this.labelRes} logo=${this.logo} metaData=${this.metaData.dumpToString()} multiprocess=${this.multiprocess} name=${this.name} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} pathPermissions=${this.pathPermissions?.joinToString { "readPermission=${it.readPermission}\nwritePermission=${it.writePermission}" }} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} readPermission=${this.readPermission} showUserIcon=${this.showUserIcon} splitName=${this.splitName} uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()} writePermission=${this.writePermission} """.trimIndent() protected fun ServiceInfo.dumpToString() = """ applicationInfo=${this.applicationInfo.ignored("Already checked")} banner=${this.banner} descriptionRes=${this.descriptionRes} directBootAware=${this.directBootAware} enabled=${this.enabled} exported=${this.exported} flags=${Integer.toBinaryString(this.flags)} icon=${this.icon} labelRes=${this.labelRes} logo=${this.logo} mForegroundServiceType"${this.mForegroundServiceType} metaData=${this.metaData.dumpToString()} name=${this.name} nonLocalizedLabel=${this.nonLocalizedLabel} packageName=${this.packageName} permission=${this.permission} processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")} showUserIcon=${this.showUserIcon} splitName=${this.splitName} """.trimIndent() protected fun ConfigurationInfo.dumpToString() = """ reqGlEsVersion=${this.reqGlEsVersion} reqInputFeatures=${this.reqInputFeatures} Loading @@ -333,8 +422,10 @@ open class AndroidPackageParsingTestBase { """.trimIndent() protected fun PackageInfo.dumpToString() = """ activities=${this.activities?.joinToString { it.dumpToString() }} applicationInfo=${this.applicationInfo.dumpToString()} activities=${this.activities?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} applicationInfo=${this.applicationInfo.dumpToString() .ignored("Checked separately in test")} baseRevisionCode=${this.baseRevisionCode} compileSdkVersion=${this.compileSdkVersion} compileSdkVersionCodename=${this.compileSdkVersionCodename} Loading @@ -356,15 +447,18 @@ open class AndroidPackageParsingTestBase { overlayTarget=${this.overlayTarget} packageName=${this.packageName} permissions=${this.permissions?.joinToString { it.dumpToString() }} providers=${this.providers?.joinToString { it.dumpToString() }} receivers=${this.receivers?.joinToString { it.dumpToString() }} providers=${this.providers?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} receivers=${this.receivers?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }} requestedPermissions=${this.requestedPermissions?.contentToString()} requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()} requiredAccountType=${this.requiredAccountType} requiredForAllUsers=${this.requiredForAllUsers} restrictedAccountType=${this.restrictedAccountType} services=${this.services?.contentToString()} services=${this.services?.joinToString { it.dumpToString() } .ignored("Checked separately in test")} sharedUserId=${this.sharedUserId} sharedUserLabel=${this.sharedUserLabel} signatures=${this.signatures?.joinToString { it.toCharsString() }} Loading @@ -378,11 +472,17 @@ open class AndroidPackageParsingTestBase { versionName=${this.versionName} """.trimIndent() @Suppress("unused") private fun <T> SparseArray<T>.sequence(): Sequence<Pair<Int, T>> { var index = 0 return generateSequence { index++.takeIf { it < size() }?.let { keyAt(it) to valueAt(index) } private fun Bundle?.dumpToString() = this?.keySet()?.associateWith { get(it) }?.toString() private fun <T> SparseArray<T>?.dumpToString(): String { if (this == null) { return "EMPTY" } val list = mutableListOf<Pair<Int, T>>() for (index in (0 until size())) { list += keyAt(index) to valueAt(index) } return list.toString() } }