Fix up discrepancies between v1 and v2 package parsing

For Activity aliases, it's possible some values are already
set, which means they cannot be assumed to be 0, and can't be
overwritten if a attribute in the alias is undefined. For the
parsing v2 refactor, this was cleaned up to avoid
redundant != 0 checks, but those checks are indeed necessary.
This copies over the old logic and uses it exactly.

In some future cleanup, there should be a more structured way
of doing this, since it's not immediately obvious which values
are overridden or not. For example, description is always
overwritten even if no new value is provided in the alias.

This also fixes up the comparison tests and other bugs that
popped up because of them. The core issue was that when
auto-generating the dumpToString methods, the Alt+Insert
macro default selects all the fields in the current class,
but not all the parent classes, so some shared fields like
name/icon were not considered.

A notable case that was found when running the comparison tests
is that persistableMode is now "fixed" with v2. Previously,
a bug in PackageParser caused this value to be dropped if
the ActivityInfo object ever had to be copied. This is a change
from Q behavior, but there's no good way to reconcile this, and
it's better to be correct and consistent than broken, so this
fix was left in and excluded from the comparison tests.

Bug: 150106908

Test: manual run through steps in bug
Test: atest com.android.server.pm.parsing

Merged-In: I1301e28540314d0e643b73af7146c1a366eca6b5
Change-Id: I1301e28540314d0e643b73af7146c1a366eca6b5
This commit is contained in:
Winson
2020-05-29 15:05:00 -07:00
committed by Winson Chiu
parent 95307df505
commit 4548256900
4 changed files with 244 additions and 57 deletions

View File

@@ -302,7 +302,14 @@ public class ParsedActivityUtils {
} }
String permission = array.getNonConfigurationString(permissionAttr, 0); String permission = array.getNonConfigurationString(permissionAttr, 0);
activity.setPermission(permission != null ? permission : pkg.getPermission()); 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); final boolean setExported = array.hasValue(exportedAttr);
if (setExported) { if (setExported) {

View File

@@ -20,7 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils; 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.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
@@ -29,9 +32,6 @@ import android.text.TextUtils;
import android.util.TypedValue; import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting; 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 */ /** @hide */
class ParsedComponentUtils { class ParsedComponentUtils {
@@ -60,16 +60,27 @@ class ParsedComponentUtils {
component.setName(className); component.setName(className);
component.setPackageName(packageName); component.setPackageName(packageName);
if (useRoundIcon) { int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
component.icon = array.getResourceId(roundIconAttr, 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) { int logoVal = array.getResourceId(logoAttr, 0);
component.icon = array.getResourceId(iconAttr, 0); if (logoVal != 0) {
component.logo = logoVal;
} }
component.logo = array.getResourceId(logoAttr, 0); int bannerVal = array.getResourceId(bannerAttr, 0);
component.banner = array.getResourceId(bannerAttr, 0); if (bannerVal != 0) {
component.banner = bannerVal;
}
if (descriptionAttr != null) { if (descriptionAttr != null) {
component.descriptionRes = array.getResourceId(descriptionAttr, 0); component.descriptionRes = array.getResourceId(descriptionAttr, 0);

View File

@@ -18,9 +18,9 @@ package com.android.server.pm.parsing
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.platform.test.annotations.Presubmit import android.platform.test.annotations.Presubmit
import androidx.test.filters.LargeTest
import com.google.common.truth.Expect import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@@ -35,7 +35,6 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
val expect = Expect.create() val expect = Expect.create()
@Test @Test
@Ignore("b/155935153")
fun applicationInfoEquality() { fun applicationInfoEquality() {
val flags = PackageManager.GET_META_DATA or PackageManager.GET_SHARED_LIBRARY_FILES val flags = PackageManager.GET_META_DATA or PackageManager.GET_SHARED_LIBRARY_FILES
val oldAppInfo = oldPackages.asSequence().map { oldAppInfo(it, flags) } val oldAppInfo = oldPackages.asSequence().map { oldAppInfo(it, flags) }
@@ -54,8 +53,8 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
} }
} }
@LargeTest
@Test @Test
@Ignore("b/155935153")
fun packageInfoEquality() { fun packageInfoEquality() {
val flags = PackageManager.GET_ACTIVITIES or val flags = PackageManager.GET_ACTIVITIES or
PackageManager.GET_CONFIGURATIONS or PackageManager.GET_CONFIGURATIONS or
@@ -68,7 +67,9 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
PackageManager.GET_SERVICES or PackageManager.GET_SERVICES or
PackageManager.GET_SHARED_LIBRARY_FILES or PackageManager.GET_SHARED_LIBRARY_FILES or
PackageManager.GET_SIGNATURES 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 oldPackageInfo = oldPackages.asSequence().map { oldPackageInfo(it, flags) }
val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) } val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) }
@@ -80,11 +81,79 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
} else { } else {
"$firstName | $secondName" "$firstName | $secondName"
} }
expect.withMessage("${it.first?.applicationInfo?.sourceDir} $packageName")
.that(it.first?.dumpToString()) // Main components are asserted independently to separate the failures. Otherwise the
.isEqualTo(it.second?.dumpToString()) // 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())
}
}
} }
} }
} }

View File

@@ -19,6 +19,7 @@ package com.android.server.pm.parsing
import android.content.Context import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.ComponentInfo
import android.content.pm.ConfigurationInfo import android.content.pm.ConfigurationInfo
import android.content.pm.FeatureInfo import android.content.pm.FeatureInfo
import android.content.pm.InstrumentationInfo import android.content.pm.InstrumentationInfo
@@ -27,6 +28,8 @@ import android.content.pm.PackageParser
import android.content.pm.PackageUserState import android.content.pm.PackageUserState
import android.content.pm.PermissionInfo import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo import android.content.pm.ProviderInfo
import android.content.pm.ServiceInfo
import android.os.Bundle
import android.os.Debug import android.os.Debug
import android.os.Environment import android.os.Environment
import android.util.SparseArray import android.util.SparseArray
@@ -38,8 +41,10 @@ import com.android.server.pm.pkg.PackageStateUnserialized
import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever import com.android.server.testutils.whenever
import org.junit.BeforeClass 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.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import java.io.File import java.io.File
@@ -47,7 +52,7 @@ open class AndroidPackageParsingTestBase {
companion object { companion object {
private const val VERIFY_ALL_APKS = false private const val VERIFY_ALL_APKS = true
/** For auditing memory usage differences */ /** For auditing memory usage differences */
private const val DUMP_HPROF_TO_EXTERNAL = false private const val DUMP_HPROF_TO_EXTERNAL = false
@@ -81,10 +86,14 @@ open class AndroidPackageParsingTestBase {
.filter { file -> file.name.endsWith(".apk") } .filter { file -> file.name.endsWith(".apk") }
.toList() .toList()
} }
.distinct()
private val dummyUserState = mock(PackageUserState::class.java).apply { private val dummyUserState = mock(PackageUserState::class.java).apply {
installed = true 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> lateinit var oldPackages: List<PackageParser.Package>
@@ -145,6 +154,7 @@ open class AndroidPackageParsingTestBase {
private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> { private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> {
this.pkg = aPkg this.pkg = aPkg
whenever(pkgState) { PackageStateUnserialized() } whenever(pkgState) { PackageStateUnserialized() }
whenever(readUserState(anyInt())) { dummyUserState }
} }
} }
@@ -156,19 +166,10 @@ open class AndroidPackageParsingTestBase {
// The following methods prepend "this." because @hide APIs can cause an IDE to auto-import // 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. // the R.attr constant instead of referencing the field in an attempt to fix the error.
/** // It's difficult to comment out a line in a triple quoted string, so this is used instead
* Known exclusions: // to ignore specific fields. A comment is required to explain why a field was ignored.
* - [ApplicationInfo.credentialProtectedDataDir] private fun Any?.ignored(comment: String): String = "IGNORED"
* - [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].
*/
protected fun ApplicationInfo.dumpToString() = """ protected fun ApplicationInfo.dumpToString() = """
appComponentFactory=${this.appComponentFactory} appComponentFactory=${this.appComponentFactory}
backupAgentName=${this.backupAgentName} backupAgentName=${this.backupAgentName}
@@ -179,22 +180,31 @@ open class AndroidPackageParsingTestBase {
compatibleWidthLimitDp=${this.compatibleWidthLimitDp} compatibleWidthLimitDp=${this.compatibleWidthLimitDp}
compileSdkVersion=${this.compileSdkVersion} compileSdkVersion=${this.compileSdkVersion}
compileSdkVersionCodename=${this.compileSdkVersionCodename} 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} descriptionRes=${this.descriptionRes}
deviceProtectedDataDir=${this.deviceProtectedDataDir
.ignored("Deferred pre-R, but assigned immediately in R")}
enabled=${this.enabled} enabled=${this.enabled}
enabledSetting=${this.enabledSetting} enabledSetting=${this.enabledSetting}
flags=${Integer.toBinaryString(this.flags)} flags=${Integer.toBinaryString(this.flags)}
fullBackupContent=${this.fullBackupContent} fullBackupContent=${this.fullBackupContent}
gwpAsanMode=${this.gwpAsanMode.ignored("Added in R")}
hiddenUntilInstalled=${this.hiddenUntilInstalled} hiddenUntilInstalled=${this.hiddenUntilInstalled}
icon=${this.icon} icon=${this.icon}
iconRes=${this.iconRes} iconRes=${this.iconRes}
installLocation=${this.installLocation} installLocation=${this.installLocation}
labelRes=${this.labelRes}
largestWidthLimitDp=${this.largestWidthLimitDp} largestWidthLimitDp=${this.largestWidthLimitDp}
logo=${this.logo} logo=${this.logo}
longVersionCode=${this.longVersionCode} longVersionCode=${this.longVersionCode}
${"".ignored("mHiddenApiPolicy is a private field")}
manageSpaceActivityName=${this.manageSpaceActivityName} manageSpaceActivityName=${this.manageSpaceActivityName}
maxAspectRatio.compareTo(that.maxAspectRatio)=${this.maxAspectRatio} maxAspectRatio=${this.maxAspectRatio}
metaData=${this.metaData} metaData=${this.metaData.dumpToString()}
minAspectRatio.compareTo(that.minAspectRatio)=${this.minAspectRatio} minAspectRatio=${this.minAspectRatio}
minSdkVersion=${this.minSdkVersion} minSdkVersion=${this.minSdkVersion}
name=${this.name} name=${this.name}
nativeLibraryDir=${this.nativeLibraryDir} nativeLibraryDir=${this.nativeLibraryDir}
@@ -206,18 +216,27 @@ open class AndroidPackageParsingTestBase {
permission=${this.permission} permission=${this.permission}
primaryCpuAbi=${this.primaryCpuAbi} primaryCpuAbi=${this.primaryCpuAbi}
privateFlags=${Integer.toBinaryString(this.privateFlags)} 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} requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
resourceDirs=${this.resourceDirs?.contentToString()} resourceDirs=${this.resourceDirs?.contentToString()}
roundIconRes=${this.roundIconRes} roundIconRes=${this.roundIconRes}
secondaryCpuAbi=${this.secondaryCpuAbi} scanPublicSourceDir=${this.scanPublicSourceDir
secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} .ignored("Deferred pre-R, but assigned immediately in R")}
scanSourceDir=${this.scanSourceDir
.ignored("Deferred pre-R, but assigned immediately in R")}
seInfo=${this.seInfo} seInfo=${this.seInfo}
seInfoUser=${this.seInfoUser} seInfoUser=${this.seInfoUser}
secondaryCpuAbi=${this.secondaryCpuAbi}
secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()} sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()}
sharedLibraryInfos=${this.sharedLibraryInfos} sharedLibraryInfos=${this.sharedLibraryInfos}
showUserIcon=${this.showUserIcon} showUserIcon=${this.showUserIcon}
sourceDir=${this.sourceDir
.ignored("Deferred pre-R, but assigned immediately in R")}
splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()} splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()}
splitDependencies=${this.splitDependencies} splitDependencies=${this.splitDependencies.dumpToString()}
splitNames=${this.splitNames?.contentToString()} splitNames=${this.splitNames?.contentToString()}
splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()} splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
splitSourceDirs=${this.splitSourceDirs?.contentToString()} splitSourceDirs=${this.splitSourceDirs?.contentToString()}
@@ -226,8 +245,8 @@ open class AndroidPackageParsingTestBase {
targetSdkVersion=${this.targetSdkVersion} targetSdkVersion=${this.targetSdkVersion}
taskAffinity=${this.taskAffinity} taskAffinity=${this.taskAffinity}
theme=${this.theme} theme=${this.theme}
uid=${this.uid}
uiOptions=${this.uiOptions} uiOptions=${this.uiOptions}
uid=${this.uid}
versionCode=${this.versionCode} versionCode=${this.versionCode}
volumeUuid=${this.volumeUuid} volumeUuid=${this.volumeUuid}
zygotePreloadName=${this.zygotePreloadName} zygotePreloadName=${this.zygotePreloadName}
@@ -241,19 +260,27 @@ open class AndroidPackageParsingTestBase {
""".trimIndent() """.trimIndent()
protected fun InstrumentationInfo.dumpToString() = """ protected fun InstrumentationInfo.dumpToString() = """
banner=${this.banner}
credentialProtectedDataDir=${this.credentialProtectedDataDir} credentialProtectedDataDir=${this.credentialProtectedDataDir}
dataDir=${this.dataDir} dataDir=${this.dataDir}
deviceProtectedDataDir=${this.deviceProtectedDataDir} deviceProtectedDataDir=${this.deviceProtectedDataDir}
functionalTest=${this.functionalTest} functionalTest=${this.functionalTest}
handleProfiling=${this.handleProfiling} handleProfiling=${this.handleProfiling}
icon=${this.icon}
labelRes=${this.labelRes}
logo=${this.logo}
metaData=${this.metaData}
name=${this.name}
nativeLibraryDir=${this.nativeLibraryDir} nativeLibraryDir=${this.nativeLibraryDir}
nonLocalizedLabel=${this.nonLocalizedLabel}
packageName=${this.packageName}
primaryCpuAbi=${this.primaryCpuAbi} primaryCpuAbi=${this.primaryCpuAbi}
publicSourceDir=${this.publicSourceDir} publicSourceDir=${this.publicSourceDir}
secondaryCpuAbi=${this.secondaryCpuAbi} secondaryCpuAbi=${this.secondaryCpuAbi}
secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir} secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
showUserIcon=${this.showUserIcon}
sourceDir=${this.sourceDir} sourceDir=${this.sourceDir}
splitDependencies=${this.splitDependencies.sequence() splitDependencies=${this.splitDependencies.dumpToString()}
.map { it.first to it.second?.contentToString() }.joinToString()}
splitNames=${this.splitNames?.contentToString()} splitNames=${this.splitNames?.contentToString()}
splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()} splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
splitSourceDirs=${this.splitSourceDirs?.contentToString()} splitSourceDirs=${this.splitSourceDirs?.contentToString()}
@@ -262,25 +289,40 @@ open class AndroidPackageParsingTestBase {
""".trimIndent() """.trimIndent()
protected fun ActivityInfo.dumpToString() = """ protected fun ActivityInfo.dumpToString() = """
banner=${this.banner}
colorMode=${this.colorMode} colorMode=${this.colorMode}
configChanges=${this.configChanges} configChanges=${this.configChanges}
descriptionRes=${this.descriptionRes}
directBootAware=${this.directBootAware}
documentLaunchMode=${this.documentLaunchMode} documentLaunchMode=${this.documentLaunchMode}
enabled=${this.enabled}
exported=${this.exported}
flags=${Integer.toBinaryString(this.flags)} flags=${Integer.toBinaryString(this.flags)}
icon=${this.icon}
labelRes=${this.labelRes}
launchMode=${this.launchMode} launchMode=${this.launchMode}
launchToken=${this.launchToken} launchToken=${this.launchToken}
lockTaskLaunchMode=${this.lockTaskLaunchMode} lockTaskLaunchMode=${this.lockTaskLaunchMode}
logo=${this.logo}
maxAspectRatio=${this.maxAspectRatio} maxAspectRatio=${this.maxAspectRatio}
maxRecents=${this.maxRecents} maxRecents=${this.maxRecents}
metaData=${this.metaData.dumpToString()}
minAspectRatio=${this.minAspectRatio} minAspectRatio=${this.minAspectRatio}
name=${this.name}
nonLocalizedLabel=${this.nonLocalizedLabel}
packageName=${this.packageName}
parentActivityName=${this.parentActivityName} parentActivityName=${this.parentActivityName}
permission=${this.permission} permission=${this.permission}
persistableMode=${this.persistableMode} persistableMode=${this.persistableMode.ignored("Could be dropped pre-R, fixed in R")}
privateFlags=${Integer.toBinaryString(this.privateFlags)} privateFlags=${this.privateFlags}
processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
requestedVrComponent=${this.requestedVrComponent} requestedVrComponent=${this.requestedVrComponent}
resizeMode=${this.resizeMode} resizeMode=${this.resizeMode}
rotationAnimation=${this.rotationAnimation} rotationAnimation=${this.rotationAnimation}
screenOrientation=${this.screenOrientation} screenOrientation=${this.screenOrientation}
showUserIcon=${this.showUserIcon}
softInputMode=${this.softInputMode} softInputMode=${this.softInputMode}
splitName=${this.splitName}
targetActivity=${this.targetActivity} targetActivity=${this.targetActivity}
taskAffinity=${this.taskAffinity} taskAffinity=${this.taskAffinity}
theme=${this.theme} theme=${this.theme}
@@ -300,30 +342,77 @@ open class AndroidPackageParsingTestBase {
protected fun PermissionInfo.dumpToString() = """ protected fun PermissionInfo.dumpToString() = """
backgroundPermission=${this.backgroundPermission} backgroundPermission=${this.backgroundPermission}
banner=${this.banner}
descriptionRes=${this.descriptionRes} descriptionRes=${this.descriptionRes}
flags=${Integer.toBinaryString(this.flags)} flags=${Integer.toBinaryString(this.flags)}
group=${this.group} group=${this.group}
icon=${this.icon}
labelRes=${this.labelRes}
logo=${this.logo}
metaData=${this.metaData.dumpToString()}
name=${this.name}
nonLocalizedDescription=${this.nonLocalizedDescription} nonLocalizedDescription=${this.nonLocalizedDescription}
nonLocalizedLabel=${this.nonLocalizedLabel}
packageName=${this.packageName}
protectionLevel=${this.protectionLevel} protectionLevel=${this.protectionLevel}
requestRes=${this.requestRes} requestRes=${this.requestRes}
showUserIcon=${this.showUserIcon}
""".trimIndent() """.trimIndent()
protected fun ProviderInfo.dumpToString() = """ protected fun ProviderInfo.dumpToString() = """
applicationInfo=${this.applicationInfo.ignored("Already checked")}
authority=${this.authority} authority=${this.authority}
banner=${this.banner}
descriptionRes=${this.descriptionRes}
directBootAware=${this.directBootAware}
enabled=${this.enabled}
exported=${this.exported}
flags=${Integer.toBinaryString(this.flags)} flags=${Integer.toBinaryString(this.flags)}
forceUriPermissions=${this.forceUriPermissions} forceUriPermissions=${this.forceUriPermissions}
grantUriPermissions=${this.grantUriPermissions} grantUriPermissions=${this.grantUriPermissions}
icon=${this.icon}
initOrder=${this.initOrder} initOrder=${this.initOrder}
isSyncable=${this.isSyncable} isSyncable=${this.isSyncable}
labelRes=${this.labelRes}
logo=${this.logo}
metaData=${this.metaData.dumpToString()}
multiprocess=${this.multiprocess} multiprocess=${this.multiprocess}
name=${this.name}
nonLocalizedLabel=${this.nonLocalizedLabel}
packageName=${this.packageName}
pathPermissions=${this.pathPermissions?.joinToString { pathPermissions=${this.pathPermissions?.joinToString {
"readPermission=${it.readPermission}\nwritePermission=${it.writePermission}" "readPermission=${it.readPermission}\nwritePermission=${it.writePermission}"
}} }}
processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
readPermission=${this.readPermission} readPermission=${this.readPermission}
showUserIcon=${this.showUserIcon}
splitName=${this.splitName}
uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()} uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()}
writePermission=${this.writePermission} writePermission=${this.writePermission}
""".trimIndent() """.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() = """ protected fun ConfigurationInfo.dumpToString() = """
reqGlEsVersion=${this.reqGlEsVersion} reqGlEsVersion=${this.reqGlEsVersion}
reqInputFeatures=${this.reqInputFeatures} reqInputFeatures=${this.reqInputFeatures}
@@ -333,8 +422,10 @@ open class AndroidPackageParsingTestBase {
""".trimIndent() """.trimIndent()
protected fun PackageInfo.dumpToString() = """ protected fun PackageInfo.dumpToString() = """
activities=${this.activities?.joinToString { it.dumpToString() }} activities=${this.activities?.joinToString { it.dumpToString() }
applicationInfo=${this.applicationInfo.dumpToString()} .ignored("Checked separately in test")}
applicationInfo=${this.applicationInfo.dumpToString()
.ignored("Checked separately in test")}
baseRevisionCode=${this.baseRevisionCode} baseRevisionCode=${this.baseRevisionCode}
compileSdkVersion=${this.compileSdkVersion} compileSdkVersion=${this.compileSdkVersion}
compileSdkVersionCodename=${this.compileSdkVersionCodename} compileSdkVersionCodename=${this.compileSdkVersionCodename}
@@ -356,15 +447,18 @@ open class AndroidPackageParsingTestBase {
overlayTarget=${this.overlayTarget} overlayTarget=${this.overlayTarget}
packageName=${this.packageName} packageName=${this.packageName}
permissions=${this.permissions?.joinToString { it.dumpToString() }} permissions=${this.permissions?.joinToString { it.dumpToString() }}
providers=${this.providers?.joinToString { it.dumpToString() }} providers=${this.providers?.joinToString { it.dumpToString() }
receivers=${this.receivers?.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() }} reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }}
requestedPermissions=${this.requestedPermissions?.contentToString()} requestedPermissions=${this.requestedPermissions?.contentToString()}
requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()} requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()}
requiredAccountType=${this.requiredAccountType} requiredAccountType=${this.requiredAccountType}
requiredForAllUsers=${this.requiredForAllUsers} requiredForAllUsers=${this.requiredForAllUsers}
restrictedAccountType=${this.restrictedAccountType} restrictedAccountType=${this.restrictedAccountType}
services=${this.services?.contentToString()} services=${this.services?.joinToString { it.dumpToString() }
.ignored("Checked separately in test")}
sharedUserId=${this.sharedUserId} sharedUserId=${this.sharedUserId}
sharedUserLabel=${this.sharedUserLabel} sharedUserLabel=${this.sharedUserLabel}
signatures=${this.signatures?.joinToString { it.toCharsString() }} signatures=${this.signatures?.joinToString { it.toCharsString() }}
@@ -378,11 +472,17 @@ open class AndroidPackageParsingTestBase {
versionName=${this.versionName} versionName=${this.versionName}
""".trimIndent() """.trimIndent()
@Suppress("unused") private fun Bundle?.dumpToString() = this?.keySet()?.associateWith { get(it) }?.toString()
private fun <T> SparseArray<T>.sequence(): Sequence<Pair<Int, T>> {
var index = 0 private fun <T> SparseArray<T>?.dumpToString(): String {
return generateSequence { if (this == null) {
index++.takeIf { it < size() }?.let { keyAt(it) to valueAt(index) } return "EMPTY"
} }
val list = mutableListOf<Pair<Int, T>>()
for (index in (0 until size())) {
list += keyAt(index) to valueAt(index)
}
return list.toString()
} }
} }