From c086fe9ac1bd33ddc6c6aa681f7c92eababdceb0 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 15 Apr 2020 12:41:03 -0700 Subject: [PATCH] Optimize (Parsing)PackageImpl implementation To speed up recurring ApplicationInfo creation, this caches the the appInfoFlags/appInfoPrivateFlags at parse time, since they're immutable at that point. This saves having to calculate it hundreds of times for all the packages in getInstalledPackages or similar. This also saves the base app data directories for the system user, or user 0, interning the strings since they're shared across packages with the same volume UUID. A better solution would cache these paths during boot scan and re-use the strings directly rather than re-building and relying on interning, but there was no good mechanism for that. This decreases string append computation time and compromises between persistent memory and performance. This also compacts boolean fields into a bitset, decreasing the amount persisted in both the object memory and the Parcelable output. In combination these changes result in a net decrease in memory usage, although the difference is neglible, on the order of ~4KB for 181 packages or ~22B a package. The increase in speed is roughly 2/3rds saved off the total time of generateWithComponents. An important note is that hideAsParsed/hideAsFinal are now required to be called because they now calculate the derived fields to be cached. Bug: 153656459 Test: atest CrossProfileAppsServiceImplRoboTest Test: atest PackageManagerComponentLabelIconOverrideTest Test: atest PackageParserTest Test: atest UserSystemPackageInstallerTest Test: atest DexoptUtilsTest Test: atest com.android.server.pm.parsing Test: atest PackageInfoUserFieldsTest Test: atest com.android.server.pm.ScanTests Test: atest android.content.pm.cts.PackageManagerTest Change-Id: I977edb9dec720893ccb1ce5b9df33733c408d3c1 --- .../parsing/PackageInfoWithoutStateUtils.java | 91 ++- .../content/pm/parsing/ParsingPackage.java | 3 +- .../pm/parsing/ParsingPackageImpl.java | 537 ++++++++---------- .../pm/parsing/ParsingPackageRead.java | 2 +- .../pm/parsing/ParsingPackageUtils.java | 3 + core/java/android/os/Environment.java | 47 +- core/java/android/os/TEST_MAPPING | 9 + .../server/pm/PackageManagerService.java | 12 +- .../com/android/server/pm/PackageSetting.java | 4 +- .../server/pm/parsing/PackageInfoUtils.java | 96 +++- .../server/pm/parsing/pkg/PackageImpl.java | 195 ++++--- .../server/pm/parsing/pkg/ParsedPackage.java | 3 + .../android/server/pm/PackageParserTest.java | 6 +- .../src/com/android/server/pm/ScanTests.java | 7 +- .../AndroidPackageInfoFlagBehaviorTest.kt | 4 +- .../parsing/AndroidPackageParsingTestBase.kt | 42 +- .../pm/parsing/PackageInfoUserFieldsTest.kt | 145 +++++ 17 files changed, 793 insertions(+), 413 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index e573539221153..c5240c2814e8b 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -63,6 +63,9 @@ import java.util.Set; /** @hide **/ public class PackageInfoWithoutStateUtils { + public static final String SYSTEM_DATA_PATH = + Environment.getDataDirectoryPath() + File.separator + "system"; + @Nullable public static PackageInfo generate(ParsingPackageRead pkg, int[] gids, @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, @@ -168,7 +171,8 @@ public class PackageInfoWithoutStateUtils { info.instrumentation = new InstrumentationInfo[N]; for (int i = 0; i < N; i++) { info.instrumentation[i] = generateInstrumentationInfo( - pkg.getInstrumentations().get(i), pkg, flags, userId); + pkg.getInstrumentations().get(i), pkg, flags, userId, + true /* assignUserFields */); } } } @@ -332,7 +336,8 @@ public class PackageInfoWithoutStateUtils { return null; } - return generateApplicationInfoUnchecked(pkg, flags, state, userId); + return generateApplicationInfoUnchecked(pkg, flags, state, userId, + true /* assignUserFields */); } /** @@ -340,15 +345,23 @@ public class PackageInfoWithoutStateUtils { * system server. * * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}. + * + * @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user + * specific fields. This can be skipped when building from a system + * server package, as there are cached strings which can be used rather + * than querying and concatenating the comparatively expensive + * {@link Environment#getDataDirectory(String)}}. */ @NonNull public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg, - @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) { + @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId, + boolean assignUserFields) { // Make shallow copy so we can store the metadata/libraries safely ApplicationInfo ai = pkg.toAppInfoWithoutState(); - // Init handles data directories - // TODO(b/135203078): Consolidate the data directory logic, remove initForUser - ai.initForUser(userId); + + if (assignUserFields) { + assignUserFields(pkg, ai, userId); + } if ((flags & PackageManager.GET_META_DATA) == 0) { ai.metaData = null; @@ -567,9 +580,14 @@ public class PackageInfoWithoutStateUtils { return generateProviderInfo(pkg, p, flags, state, null, userId); } + /** + * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead, int, + * PackageUserState, int, boolean)} + */ @Nullable public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i, - ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId) { + ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId, + boolean assignUserFields) { if (i == null) return null; InstrumentationInfo ii = new InstrumentationInfo(); @@ -585,10 +603,10 @@ public class PackageInfoWithoutStateUtils { ii.splitSourceDirs = pkg.getSplitCodePaths(); ii.splitPublicSourceDirs = pkg.getSplitCodePaths(); ii.splitDependencies = pkg.getSplitDependencies(); - ii.dataDir = getDataDir(pkg, userId).getAbsolutePath(); - ii.deviceProtectedDataDir = getDeviceProtectedDataDir(pkg, userId).getAbsolutePath(); - ii.credentialProtectedDataDir = getCredentialProtectedDataDir(pkg, - userId).getAbsolutePath(); + + if (assignUserFields) { + assignUserFields(pkg, ii, userId); + } if ((flags & PackageManager.GET_META_DATA) == 0) { return ii; @@ -770,4 +788,55 @@ public class PackageInfoWithoutStateUtils { return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId, pkg.getPackageName()); } + + private static void assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId) { + // This behavior is undefined for no-state ApplicationInfos when called by a public API, + // since the uid is never assigned by the system. It will always effectively be appId 0. + info.uid = UserHandle.getUid(userId, UserHandle.getAppId(info.uid)); + + String pkgName = pkg.getPackageName(); + if ("android".equals(pkgName)) { + info.dataDir = SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + String baseDataDirPrefix = + Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator; + String userIdPkgSuffix = File.separator + userId + File.separator + pkgName; + info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE + + userIdPkgSuffix; + info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix; + + if (pkg.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + info.dataDir = info.deviceProtectedDataDir; + } else { + info.dataDir = info.credentialProtectedDataDir; + } + } + + private static void assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info, + int userId) { + String pkgName = pkg.getPackageName(); + if ("android".equals(pkgName)) { + info.dataDir = SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + String baseDataDirPrefix = + Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator; + String userIdPkgSuffix = File.separator + userId + File.separator + pkgName; + info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE + + userIdPkgSuffix; + info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix; + + if (pkg.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + info.dataDir = info.deviceProtectedDataDir; + } else { + info.dataDir = info.credentialProtectedDataDir; + } + } } diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 872098c8689ef..9cda4d33503b3 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -16,6 +16,7 @@ package android.content.pm.parsing; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; @@ -343,6 +344,6 @@ public interface ParsingPackage extends ParsingPackageRead { // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement // for moving to the next step - @Deprecated + @CallSuper Object hideAsParsed(); } diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index ed12a17ad493b..2a15e0260c1a3 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -20,6 +20,8 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; +import android.annotation.CallSuper; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; @@ -73,6 +75,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; /** * The backing data for a package that was parsed from disk. @@ -147,7 +150,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull protected String mBaseApkPath; - private boolean requiredForAllUsers; @Nullable @DataClass.ParcelWith(ForInternedString.class) private String restrictedAccountType; @@ -165,7 +167,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForInternedString.class) private String overlayCategory; private int overlayPriority; - private boolean overlayIsStatic; @NonNull @DataClass.ParcelWith(ForInternedStringValueMap.class) private Map overlayables = emptyMap(); @@ -282,11 +283,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForInternedString.class) protected String mPath; - private boolean use32BitAbi; - private boolean visibleToInstantApps; - - private boolean forceQueryable; - @NonNull @DataClass.ParcelWith(ForInternedStringList.class) private List queriesIntents = emptyList(); @@ -331,12 +327,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private int compatibleWidthLimitDp; private int descriptionRes; - // Usually there's code to set this to true during parsing, but it's possible to install an APK - // targeting tag. That code would be skipped and never - // assign this, so initialize this to true for those cases. - private boolean enabled = true; - - private boolean crossProfile; private int fullBackupContent; private int iconRes; private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION; @@ -374,26 +364,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForInternedString.class) private String zygotePreloadName; - private boolean externalStorage; - private boolean baseHardwareAccelerated; - private boolean allowBackup; - private boolean killAfterRestore; - private boolean restoreAnyVersion; - private boolean fullBackupOnly; - private boolean persistent; - private boolean debuggable; - private boolean vmSafeMode; - private boolean hasCode; - private boolean allowTaskReparenting; - private boolean allowClearUserData; - private boolean largeHeap; - private boolean usesCleartextTraffic; - private boolean supportsRtl; - private boolean testOnly; - private boolean multiArch; - private boolean extractNativeLibs; - private boolean game; - /** * @see ParsingPackageRead#getResizeableActivity() */ @@ -401,26 +371,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @DataClass.ParcelWith(ForBoolean.class) private Boolean resizeableActivity; - private boolean staticSharedLibrary; - private boolean overlay; - private boolean isolatedSplitLoading; - private boolean hasDomainUrls; - private boolean profileableByShell; - private boolean backupInForeground; - private boolean useEmbeddedDex; - private boolean defaultToDeviceProtectedStorage; - private boolean directBootAware; - private boolean partiallyDirectBootAware; - private boolean resizeableActivityViaSdkVersion; - private boolean allowClearUserDataOnFailedRestore; - private boolean allowAudioPlaybackCapture; - private boolean requestLegacyExternalStorage; - private boolean usesNonSdkApi; - private boolean hasFragileUserData; - private boolean cantSaveState; - private boolean allowNativeHeapPointerTagging; private int autoRevokePermissions; - private boolean preserveLegacyExternalStorage; protected int gwpAsanMode; @@ -428,6 +379,130 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Nullable private ArraySet mimeGroups; + // Usually there's code to set enabled to true during parsing, but it's possible to install + // an APK targeting tag. That code would be skipped + // and never assign this, so initialize this to true for those cases. + private long mBooleans = Booleans.ENABLED; + + /** + * Flags used for a internal bitset. These flags should never be persisted or exposed outside + * of this class. It is expected that PackageCacher explicitly clears itself whenever the + * Parcelable implementation changes such that all these flags can be re-ordered or invalidated. + */ + protected static class Booleans { + @LongDef({ + EXTERNAL_STORAGE, + BASE_HARDWARE_ACCELERATED, + ALLOW_BACKUP, + KILL_AFTER_RESTORE, + RESTORE_ANY_VERSION, + FULL_BACKUP_ONLY, + PERSISTENT, + DEBUGGABLE, + VM_SAFE_MODE, + HAS_CODE, + ALLOW_TASK_REPARENTING, + ALLOW_CLEAR_USER_DATA, + LARGE_HEAP, + USES_CLEARTEXT_TRAFFIC, + SUPPORTS_RTL, + TEST_ONLY, + MULTI_ARCH, + EXTRACT_NATIVE_LIBS, + GAME, + STATIC_SHARED_LIBRARY, + OVERLAY, + ISOLATED_SPLIT_LOADING, + HAS_DOMAIN_URLS, + PROFILEABLE_BY_SHELL, + BACKUP_IN_FOREGROUND, + USE_EMBEDDED_DEX, + DEFAULT_TO_DEVICE_PROTECTED_STORAGE, + DIRECT_BOOT_AWARE, + PARTIALLY_DIRECT_BOOT_AWARE, + RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, + ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, + ALLOW_AUDIO_PLAYBACK_CAPTURE, + REQUEST_LEGACY_EXTERNAL_STORAGE, + USES_NON_SDK_API, + HAS_FRAGILE_USER_DATA, + CANT_SAVE_STATE, + ALLOW_NATIVE_HEAP_POINTER_TAGGING, + PRESERVE_LEGACY_EXTERNAL_STORAGE, + REQUIRED_FOR_ALL_USERS, + OVERLAY_IS_STATIC, + USE_32_BIT_ABI, + VISIBLE_TO_INSTANT_APPS, + FORCE_QUERYABLE, + CROSS_PROFILE, + ENABLED, + }) + public @interface Values {} + private static final long EXTERNAL_STORAGE = 1L; + private static final long BASE_HARDWARE_ACCELERATED = 1L << 1; + private static final long ALLOW_BACKUP = 1L << 2; + private static final long KILL_AFTER_RESTORE = 1L << 3; + private static final long RESTORE_ANY_VERSION = 1L << 4; + private static final long FULL_BACKUP_ONLY = 1L << 5; + private static final long PERSISTENT = 1L << 6; + private static final long DEBUGGABLE = 1L << 7; + private static final long VM_SAFE_MODE = 1L << 8; + private static final long HAS_CODE = 1L << 9; + private static final long ALLOW_TASK_REPARENTING = 1L << 10; + private static final long ALLOW_CLEAR_USER_DATA = 1L << 11; + private static final long LARGE_HEAP = 1L << 12; + private static final long USES_CLEARTEXT_TRAFFIC = 1L << 13; + private static final long SUPPORTS_RTL = 1L << 14; + private static final long TEST_ONLY = 1L << 15; + private static final long MULTI_ARCH = 1L << 16; + private static final long EXTRACT_NATIVE_LIBS = 1L << 17; + private static final long GAME = 1L << 18; + private static final long STATIC_SHARED_LIBRARY = 1L << 19; + private static final long OVERLAY = 1L << 20; + private static final long ISOLATED_SPLIT_LOADING = 1L << 21; + private static final long HAS_DOMAIN_URLS = 1L << 22; + private static final long PROFILEABLE_BY_SHELL = 1L << 23; + private static final long BACKUP_IN_FOREGROUND = 1L << 24; + private static final long USE_EMBEDDED_DEX = 1L << 25; + private static final long DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1L << 26; + private static final long DIRECT_BOOT_AWARE = 1L << 27; + private static final long PARTIALLY_DIRECT_BOOT_AWARE = 1L << 28; + private static final long RESIZEABLE_ACTIVITY_VIA_SDK_VERSION = 1L << 29; + private static final long ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1L << 30; + private static final long ALLOW_AUDIO_PLAYBACK_CAPTURE = 1L << 31; + private static final long REQUEST_LEGACY_EXTERNAL_STORAGE = 1L << 32; + private static final long USES_NON_SDK_API = 1L << 33; + private static final long HAS_FRAGILE_USER_DATA = 1L << 34; + private static final long CANT_SAVE_STATE = 1L << 35; + private static final long ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1L << 36; + private static final long PRESERVE_LEGACY_EXTERNAL_STORAGE = 1L << 37; + private static final long REQUIRED_FOR_ALL_USERS = 1L << 38; + private static final long OVERLAY_IS_STATIC = 1L << 39; + private static final long USE_32_BIT_ABI = 1L << 40; + private static final long VISIBLE_TO_INSTANT_APPS = 1L << 41; + private static final long FORCE_QUERYABLE = 1L << 42; + private static final long CROSS_PROFILE = 1L << 43; + private static final long ENABLED = 1L << 44; + } + + private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) { + if (value) { + mBooleans |= flag; + } else { + mBooleans &= ~flag; + } + return this; + } + + private boolean getBoolean(@Booleans.Values long flag) { + return (mBooleans & flag) != 0; + } + + // Derived fields + @NonNull + private UUID mStorageUuid; + private long mLongVersionCode; + @VisibleForTesting public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath, @NonNull String path, @Nullable TypedArray manifestArray) { @@ -517,10 +592,16 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return this; } + @CallSuper @Override public Object hideAsParsed() { - // There is no equivalent for core-only parsing - throw new UnsupportedOperationException(); + assignDerivedFields(); + return this; + } + + private void assignDerivedFields() { + mStorageUuid = StorageManager.convert(volumeUuid); + mLongVersionCode = PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); } @Override @@ -862,6 +943,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return this; } + @Override + public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) { + nonLocalizedLabel = value == null ? null : value.toString().trim(); + return this; + } + @NonNull @Override public String getProcessName() { @@ -905,7 +992,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { // appInfo.dataDir appInfo.descriptionRes = descriptionRes; // appInfo.deviceProtectedDataDir - appInfo.enabled = enabled; + appInfo.enabled = getBoolean(Booleans.ENABLED); // appInfo.enabledSetting appInfo.fullBackupContent = fullBackupContent; // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy @@ -924,17 +1011,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.minAspectRatio = minAspectRatio; appInfo.minSdkVersion = minSdkVersion; appInfo.name = className; - if (appInfo.name != null) { - appInfo.name = appInfo.name.trim(); - } // appInfo.nativeLibraryDir // appInfo.nativeLibraryRootDir // appInfo.nativeLibraryRootRequiresIsa appInfo.networkSecurityConfigRes = networkSecurityConfigRes; appInfo.nonLocalizedLabel = nonLocalizedLabel; - if (appInfo.nonLocalizedLabel != null) { - appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim(); - } appInfo.packageName = packageName; appInfo.permission = permission; // appInfo.primaryCpuAbi @@ -951,7 +1032,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.splitClassLoaderNames = splitClassLoaderNames; appInfo.splitDependencies = splitDependencies; appInfo.splitNames = splitNames; - appInfo.storageUuid = StorageManager.convert(volumeUuid); + appInfo.storageUuid = mStorageUuid; appInfo.targetSandboxVersion = targetSandboxVersion; appInfo.targetSdkVersion = targetSdkVersion; appInfo.taskAffinity = taskAffinity; @@ -967,7 +1048,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.setResourcePath(mPath); appInfo.setSplitCodePaths(splitCodePaths); appInfo.setSplitResourcePaths(splitCodePaths); - appInfo.setVersionCode(PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode)); + appInfo.setVersionCode(mLongVersionCode); return appInfo; } @@ -994,14 +1075,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { sForInternedString.parcel(this.packageName, dest, flags); dest.writeString(this.realPackage); dest.writeString(this.mBaseApkPath); - dest.writeBoolean(this.requiredForAllUsers); dest.writeString(this.restrictedAccountType); dest.writeString(this.requiredAccountType); sForInternedString.parcel(this.overlayTarget, dest, flags); dest.writeString(this.overlayTargetName); dest.writeString(this.overlayCategory); dest.writeInt(this.overlayPriority); - dest.writeBoolean(this.overlayIsStatic); sForInternedStringValueMap.parcel(this.overlayables, dest, flags); sForInternedString.parcel(this.staticSharedLibName, dest, flags); dest.writeLong(this.staticSharedLibVersion); @@ -1049,9 +1128,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { sForInternedString.parcel(this.volumeUuid, dest, flags); dest.writeParcelable(this.signingDetails, flags); dest.writeString(this.mPath); - dest.writeBoolean(this.use32BitAbi); - dest.writeBoolean(this.visibleToInstantApps); - dest.writeBoolean(this.forceQueryable); dest.writeParcelableList(this.queriesIntents, flags); sForInternedStringList.parcel(this.queriesPackages, dest, flags); dest.writeString(this.appComponentFactory); @@ -1062,8 +1138,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeString(this.className); dest.writeInt(this.compatibleWidthLimitDp); dest.writeInt(this.descriptionRes); - dest.writeBoolean(this.enabled); - dest.writeBoolean(this.crossProfile); dest.writeInt(this.fullBackupContent); dest.writeInt(this.iconRes); dest.writeInt(this.installLocation); @@ -1092,52 +1166,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeIntArray(this.splitFlags); dest.writeStringArray(this.splitNames); dest.writeIntArray(this.splitRevisionCodes); - - dest.writeBoolean(this.externalStorage); - dest.writeBoolean(this.baseHardwareAccelerated); - dest.writeBoolean(this.allowBackup); - dest.writeBoolean(this.killAfterRestore); - dest.writeBoolean(this.restoreAnyVersion); - dest.writeBoolean(this.fullBackupOnly); - dest.writeBoolean(this.persistent); - dest.writeBoolean(this.debuggable); - dest.writeBoolean(this.vmSafeMode); - dest.writeBoolean(this.hasCode); - dest.writeBoolean(this.allowTaskReparenting); - dest.writeBoolean(this.allowClearUserData); - dest.writeBoolean(this.largeHeap); - dest.writeBoolean(this.usesCleartextTraffic); - dest.writeBoolean(this.supportsRtl); - dest.writeBoolean(this.testOnly); - dest.writeBoolean(this.multiArch); - dest.writeBoolean(this.extractNativeLibs); - dest.writeBoolean(this.game); - sForBoolean.parcel(this.resizeableActivity, dest, flags); - - dest.writeBoolean(this.staticSharedLibrary); - dest.writeBoolean(this.overlay); - dest.writeBoolean(this.isolatedSplitLoading); - dest.writeBoolean(this.hasDomainUrls); - dest.writeBoolean(this.profileableByShell); - dest.writeBoolean(this.backupInForeground); - dest.writeBoolean(this.useEmbeddedDex); - dest.writeBoolean(this.defaultToDeviceProtectedStorage); - dest.writeBoolean(this.directBootAware); - dest.writeBoolean(this.partiallyDirectBootAware); - dest.writeBoolean(this.resizeableActivityViaSdkVersion); - dest.writeBoolean(this.allowClearUserDataOnFailedRestore); - dest.writeBoolean(this.allowAudioPlaybackCapture); - dest.writeBoolean(this.requestLegacyExternalStorage); - dest.writeBoolean(this.usesNonSdkApi); - dest.writeBoolean(this.hasFragileUserData); - dest.writeBoolean(this.cantSaveState); - dest.writeBoolean(this.allowNativeHeapPointerTagging); dest.writeInt(this.autoRevokePermissions); - dest.writeBoolean(this.preserveLegacyExternalStorage); dest.writeArraySet(this.mimeGroups); dest.writeInt(this.gwpAsanMode); dest.writeSparseIntArray(this.minExtensionVersions); + dest.writeLong(this.mBooleans); } public ParsingPackageImpl(Parcel in) { @@ -1158,14 +1192,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.packageName = sForInternedString.unparcel(in); this.realPackage = in.readString(); this.mBaseApkPath = in.readString(); - this.requiredForAllUsers = in.readBoolean(); this.restrictedAccountType = in.readString(); this.requiredAccountType = in.readString(); this.overlayTarget = sForInternedString.unparcel(in); this.overlayTargetName = in.readString(); this.overlayCategory = in.readString(); this.overlayPriority = in.readInt(); - this.overlayIsStatic = in.readBoolean(); this.overlayables = sForInternedStringValueMap.unparcel(in); this.staticSharedLibName = sForInternedString.unparcel(in); this.staticSharedLibVersion = in.readLong(); @@ -1213,9 +1245,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.volumeUuid = sForInternedString.unparcel(in); this.signingDetails = in.readParcelable(boot); this.mPath = in.readString(); - this.use32BitAbi = in.readBoolean(); - this.visibleToInstantApps = in.readBoolean(); - this.forceQueryable = in.readBoolean(); this.queriesIntents = in.createTypedArrayList(Intent.CREATOR); this.queriesPackages = sForInternedStringList.unparcel(in); this.appComponentFactory = in.readString(); @@ -1226,8 +1255,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.className = in.readString(); this.compatibleWidthLimitDp = in.readInt(); this.descriptionRes = in.readInt(); - this.enabled = in.readBoolean(); - this.crossProfile = in.readBoolean(); this.fullBackupContent = in.readInt(); this.iconRes = in.readInt(); this.installLocation = in.readInt(); @@ -1256,50 +1283,15 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.splitFlags = in.createIntArray(); this.splitNames = in.createStringArray(); this.splitRevisionCodes = in.createIntArray(); - this.externalStorage = in.readBoolean(); - this.baseHardwareAccelerated = in.readBoolean(); - this.allowBackup = in.readBoolean(); - this.killAfterRestore = in.readBoolean(); - this.restoreAnyVersion = in.readBoolean(); - this.fullBackupOnly = in.readBoolean(); - this.persistent = in.readBoolean(); - this.debuggable = in.readBoolean(); - this.vmSafeMode = in.readBoolean(); - this.hasCode = in.readBoolean(); - this.allowTaskReparenting = in.readBoolean(); - this.allowClearUserData = in.readBoolean(); - this.largeHeap = in.readBoolean(); - this.usesCleartextTraffic = in.readBoolean(); - this.supportsRtl = in.readBoolean(); - this.testOnly = in.readBoolean(); - this.multiArch = in.readBoolean(); - this.extractNativeLibs = in.readBoolean(); - this.game = in.readBoolean(); this.resizeableActivity = sForBoolean.unparcel(in); - this.staticSharedLibrary = in.readBoolean(); - this.overlay = in.readBoolean(); - this.isolatedSplitLoading = in.readBoolean(); - this.hasDomainUrls = in.readBoolean(); - this.profileableByShell = in.readBoolean(); - this.backupInForeground = in.readBoolean(); - this.useEmbeddedDex = in.readBoolean(); - this.defaultToDeviceProtectedStorage = in.readBoolean(); - this.directBootAware = in.readBoolean(); - this.partiallyDirectBootAware = in.readBoolean(); - this.resizeableActivityViaSdkVersion = in.readBoolean(); - this.allowClearUserDataOnFailedRestore = in.readBoolean(); - this.allowAudioPlaybackCapture = in.readBoolean(); - this.requestLegacyExternalStorage = in.readBoolean(); - this.usesNonSdkApi = in.readBoolean(); - this.hasFragileUserData = in.readBoolean(); - this.cantSaveState = in.readBoolean(); - this.allowNativeHeapPointerTagging = in.readBoolean(); this.autoRevokePermissions = in.readInt(); - this.preserveLegacyExternalStorage = in.readBoolean(); this.mimeGroups = (ArraySet) in.readArraySet(boot); this.gwpAsanMode = in.readInt(); this.minExtensionVersions = in.readSparseIntArray(); + this.mBooleans = in.readLong(); + + assignDerivedFields(); } public static final Parcelable.Creator CREATOR = @@ -1367,7 +1359,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isRequiredForAllUsers() { - return requiredForAllUsers; + return getBoolean(Booleans.REQUIRED_FOR_ALL_USERS); } @Nullable @@ -1407,7 +1399,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isOverlayIsStatic() { - return overlayIsStatic; + return getBoolean(Booleans.OVERLAY_IS_STATIC); } @NonNull @@ -1653,17 +1645,17 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isUse32BitAbi() { - return use32BitAbi; + return getBoolean(Booleans.USE_32_BIT_ABI); } @Override public boolean isVisibleToInstantApps() { - return visibleToInstantApps; + return getBoolean(Booleans.VISIBLE_TO_INSTANT_APPS); } @Override public boolean isForceQueryable() { - return forceQueryable; + return getBoolean(Booleans.FORCE_QUERYABLE); } @NonNull @@ -1766,12 +1758,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isEnabled() { - return enabled; + return getBoolean(Booleans.ENABLED); } @Override public boolean isCrossProfile() { - return crossProfile; + return getBoolean(Booleans.CROSS_PROFILE); } @Override @@ -1892,97 +1884,97 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isExternalStorage() { - return externalStorage; + return getBoolean(Booleans.EXTERNAL_STORAGE); } @Override public boolean isBaseHardwareAccelerated() { - return baseHardwareAccelerated; + return getBoolean(Booleans.BASE_HARDWARE_ACCELERATED); } @Override public boolean isAllowBackup() { - return allowBackup; + return getBoolean(Booleans.ALLOW_BACKUP); } @Override public boolean isKillAfterRestore() { - return killAfterRestore; + return getBoolean(Booleans.KILL_AFTER_RESTORE); } @Override public boolean isRestoreAnyVersion() { - return restoreAnyVersion; + return getBoolean(Booleans.RESTORE_ANY_VERSION); } @Override public boolean isFullBackupOnly() { - return fullBackupOnly; + return getBoolean(Booleans.FULL_BACKUP_ONLY); } @Override public boolean isPersistent() { - return persistent; + return getBoolean(Booleans.PERSISTENT); } @Override public boolean isDebuggable() { - return debuggable; + return getBoolean(Booleans.DEBUGGABLE); } @Override public boolean isVmSafeMode() { - return vmSafeMode; + return getBoolean(Booleans.VM_SAFE_MODE); } @Override public boolean isHasCode() { - return hasCode; + return getBoolean(Booleans.HAS_CODE); } @Override public boolean isAllowTaskReparenting() { - return allowTaskReparenting; + return getBoolean(Booleans.ALLOW_TASK_REPARENTING); } @Override public boolean isAllowClearUserData() { - return allowClearUserData; + return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA); } @Override public boolean isLargeHeap() { - return largeHeap; + return getBoolean(Booleans.LARGE_HEAP); } @Override public boolean isUsesCleartextTraffic() { - return usesCleartextTraffic; + return getBoolean(Booleans.USES_CLEARTEXT_TRAFFIC); } @Override public boolean isSupportsRtl() { - return supportsRtl; + return getBoolean(Booleans.SUPPORTS_RTL); } @Override public boolean isTestOnly() { - return testOnly; + return getBoolean(Booleans.TEST_ONLY); } @Override public boolean isMultiArch() { - return multiArch; + return getBoolean(Booleans.MULTI_ARCH); } @Override public boolean isExtractNativeLibs() { - return extractNativeLibs; + return getBoolean(Booleans.EXTRACT_NATIVE_LIBS); } @Override public boolean isGame() { - return game; + return getBoolean(Booleans.GAME); } /** @@ -1996,47 +1988,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isStaticSharedLibrary() { - return staticSharedLibrary; + return getBoolean(Booleans.STATIC_SHARED_LIBRARY); } @Override public boolean isOverlay() { - return overlay; + return getBoolean(Booleans.OVERLAY); } @Override public boolean isIsolatedSplitLoading() { - return isolatedSplitLoading; + return getBoolean(Booleans.ISOLATED_SPLIT_LOADING); } @Override public boolean isHasDomainUrls() { - return hasDomainUrls; + return getBoolean(Booleans.HAS_DOMAIN_URLS); } @Override public boolean isProfileableByShell() { - return profileableByShell; + return getBoolean(Booleans.PROFILEABLE_BY_SHELL); } @Override public boolean isBackupInForeground() { - return backupInForeground; + return getBoolean(Booleans.BACKUP_IN_FOREGROUND); } @Override public boolean isUseEmbeddedDex() { - return useEmbeddedDex; + return getBoolean(Booleans.USE_EMBEDDED_DEX); } @Override public boolean isDefaultToDeviceProtectedStorage() { - return defaultToDeviceProtectedStorage; + return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE); } @Override public boolean isDirectBootAware() { - return directBootAware; + return getBoolean(Booleans.DIRECT_BOOT_AWARE); } @Override @@ -2046,47 +2038,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean isPartiallyDirectBootAware() { - return partiallyDirectBootAware; + return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE); } @Override public boolean isResizeableActivityViaSdkVersion() { - return resizeableActivityViaSdkVersion; + return getBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION); } @Override public boolean isAllowClearUserDataOnFailedRestore() { - return allowClearUserDataOnFailedRestore; + return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE); } @Override public boolean isAllowAudioPlaybackCapture() { - return allowAudioPlaybackCapture; + return getBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE); } @Override public boolean isRequestLegacyExternalStorage() { - return requestLegacyExternalStorage; + return getBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE); } @Override public boolean isUsesNonSdkApi() { - return usesNonSdkApi; + return getBoolean(Booleans.USES_NON_SDK_API); } @Override public boolean isHasFragileUserData() { - return hasFragileUserData; + return getBoolean(Booleans.HAS_FRAGILE_USER_DATA); } @Override public boolean isCantSaveState() { - return cantSaveState; + return getBoolean(Booleans.CANT_SAVE_STATE); } @Override public boolean isAllowNativeHeapPointerTagging() { - return allowNativeHeapPointerTagging; + return getBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING); } @Override @@ -2096,7 +2088,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public boolean hasPreserveLegacyExternalStorage() { - return preserveLegacyExternalStorage; + return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE); } @Override @@ -2113,8 +2105,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setRequiredForAllUsers(boolean value) { - requiredForAllUsers = value; - return this; + return setBoolean(Booleans.REQUIRED_FOR_ALL_USERS, value); } @Override @@ -2125,8 +2116,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setOverlayIsStatic(boolean value) { - overlayIsStatic = value; - return this; + return setBoolean(Booleans.OVERLAY_IS_STATIC, value); } @Override @@ -2173,20 +2163,17 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setUse32BitAbi(boolean value) { - use32BitAbi = value; - return this; + return setBoolean(Booleans.USE_32_BIT_ABI, value); } @Override public ParsingPackageImpl setVisibleToInstantApps(boolean value) { - visibleToInstantApps = value; - return this; + return setBoolean(Booleans.VISIBLE_TO_INSTANT_APPS, value); } @Override public ParsingPackageImpl setForceQueryable(boolean value) { - forceQueryable = value; - return this; + return setBoolean(Booleans.FORCE_QUERYABLE, value); } @Override @@ -2215,14 +2202,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setEnabled(boolean value) { - enabled = value; - return this; + return setBoolean(Booleans.ENABLED, value); } @Override public ParsingPackageImpl setCrossProfile(boolean value) { - crossProfile = value; - return this; + return setBoolean(Booleans.CROSS_PROFILE, value); } @Override @@ -2291,12 +2276,6 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return this; } - @Override - public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) { - nonLocalizedLabel = value; - return this; - } - @Override public ParsingPackageImpl setRequiresSmallestWidthDp(int value) { requiresSmallestWidthDp = value; @@ -2335,116 +2314,97 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setExternalStorage(boolean value) { - externalStorage = value; - return this; + return setBoolean(Booleans.EXTERNAL_STORAGE, value); } @Override public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) { - baseHardwareAccelerated = value; - return this; + return setBoolean(Booleans.BASE_HARDWARE_ACCELERATED, value); } @Override public ParsingPackageImpl setAllowBackup(boolean value) { - allowBackup = value; - return this; + return setBoolean(Booleans.ALLOW_BACKUP, value); } @Override public ParsingPackageImpl setKillAfterRestore(boolean value) { - killAfterRestore = value; - return this; + return setBoolean(Booleans.KILL_AFTER_RESTORE, value); } @Override public ParsingPackageImpl setRestoreAnyVersion(boolean value) { - restoreAnyVersion = value; - return this; + return setBoolean(Booleans.RESTORE_ANY_VERSION, value); } @Override public ParsingPackageImpl setFullBackupOnly(boolean value) { - fullBackupOnly = value; - return this; + return setBoolean(Booleans.FULL_BACKUP_ONLY, value); } @Override public ParsingPackageImpl setPersistent(boolean value) { - persistent = value; - return this; + return setBoolean(Booleans.PERSISTENT, value); } @Override public ParsingPackageImpl setDebuggable(boolean value) { - debuggable = value; - return this; + return setBoolean(Booleans.DEBUGGABLE, value); } @Override public ParsingPackageImpl setVmSafeMode(boolean value) { - vmSafeMode = value; - return this; + return setBoolean(Booleans.VM_SAFE_MODE, value); } @Override public ParsingPackageImpl setHasCode(boolean value) { - hasCode = value; - return this; + return setBoolean(Booleans.HAS_CODE, value); } @Override public ParsingPackageImpl setAllowTaskReparenting(boolean value) { - allowTaskReparenting = value; - return this; + return setBoolean(Booleans.ALLOW_TASK_REPARENTING, value); } @Override public ParsingPackageImpl setAllowClearUserData(boolean value) { - allowClearUserData = value; - return this; + return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA, value); } @Override public ParsingPackageImpl setLargeHeap(boolean value) { - largeHeap = value; - return this; + return setBoolean(Booleans.LARGE_HEAP, value); } @Override public ParsingPackageImpl setUsesCleartextTraffic(boolean value) { - usesCleartextTraffic = value; - return this; + return setBoolean(Booleans.USES_CLEARTEXT_TRAFFIC, value); } @Override public ParsingPackageImpl setSupportsRtl(boolean value) { - supportsRtl = value; - return this; + return setBoolean(Booleans.SUPPORTS_RTL, value); } @Override public ParsingPackageImpl setTestOnly(boolean value) { - testOnly = value; - return this; + return setBoolean(Booleans.TEST_ONLY, value); } @Override public ParsingPackageImpl setMultiArch(boolean value) { - multiArch = value; - return this; + return setBoolean(Booleans.MULTI_ARCH, value); } @Override public ParsingPackageImpl setExtractNativeLibs(boolean value) { - extractNativeLibs = value; - return this; + return setBoolean(Booleans.EXTRACT_NATIVE_LIBS, value); } @Override public ParsingPackageImpl setGame(boolean value) { - game = value; - return this; + return setBoolean(Booleans.GAME, value); } /** @@ -2458,56 +2418,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setStaticSharedLibrary(boolean value) { - staticSharedLibrary = value; - return this; + return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value); } @Override public ParsingPackageImpl setOverlay(boolean value) { - overlay = value; - return this; + return setBoolean(Booleans.OVERLAY, value); } @Override public ParsingPackageImpl setIsolatedSplitLoading(boolean value) { - isolatedSplitLoading = value; - return this; + return setBoolean(Booleans.ISOLATED_SPLIT_LOADING, value); } @Override public ParsingPackageImpl setHasDomainUrls(boolean value) { - hasDomainUrls = value; - return this; + return setBoolean(Booleans.HAS_DOMAIN_URLS, value); } @Override public ParsingPackageImpl setProfileableByShell(boolean value) { - profileableByShell = value; - return this; + return setBoolean(Booleans.PROFILEABLE_BY_SHELL, value); } @Override public ParsingPackageImpl setBackupInForeground(boolean value) { - backupInForeground = value; - return this; + return setBoolean(Booleans.BACKUP_IN_FOREGROUND, value); } @Override public ParsingPackageImpl setUseEmbeddedDex(boolean value) { - useEmbeddedDex = value; - return this; + return setBoolean(Booleans.USE_EMBEDDED_DEX, value); } @Override public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) { - defaultToDeviceProtectedStorage = value; - return this; + return setBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE, value); } @Override public ParsingPackageImpl setDirectBootAware(boolean value) { - directBootAware = value; - return this; + return setBoolean(Booleans.DIRECT_BOOT_AWARE, value); } @Override @@ -2518,56 +2469,47 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) { - partiallyDirectBootAware = value; - return this; + return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value); } @Override public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) { - resizeableActivityViaSdkVersion = value; - return this; + return setBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, value); } @Override public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) { - allowClearUserDataOnFailedRestore = value; - return this; + return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, value); } @Override public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) { - allowAudioPlaybackCapture = value; - return this; + return setBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE, value); } @Override public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) { - requestLegacyExternalStorage = value; - return this; + return setBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE, value); } @Override public ParsingPackageImpl setUsesNonSdkApi(boolean value) { - usesNonSdkApi = value; - return this; + return setBoolean(Booleans.USES_NON_SDK_API, value); } @Override public ParsingPackageImpl setHasFragileUserData(boolean value) { - hasFragileUserData = value; - return this; + return setBoolean(Booleans.HAS_FRAGILE_USER_DATA, value); } @Override public ParsingPackageImpl setCantSaveState(boolean value) { - cantSaveState = value; - return this; + return setBoolean(Booleans.CANT_SAVE_STATE, value); } @Override public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) { - allowNativeHeapPointerTagging = value; - return this; + return setBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING, value); } @Override @@ -2578,8 +2520,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) { - preserveLegacyExternalStorage = value; - return this; + return setBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE, value); } @Override @@ -2644,7 +2585,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl setClassName(@Nullable String className) { - this.className = className; + this.className = className == null ? null : className.trim(); return this; } diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index dbd15f544bcd3..acd6305a1442e 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -867,7 +867,7 @@ public interface ParsingPackageRead extends Parcelable { * @see ApplicationInfo#gwpAsanMode * @see R.styleable#AndroidManifest_gwpAsanMode */ - public int getGwpAsanMode(); + int getGwpAsanMode(); // TODO(b/135203078): Hide and enforce going through PackageInfoUtils ApplicationInfo toAppInfoWithoutState(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index bce75cde20c2f..9197020e1fce7 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -185,6 +185,9 @@ public class ParsingPackageUtils { ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */)); } + // Need to call this to finish the parsing stage + pkg.hideAsParsed(); + return input.success(pkg); } catch (PackageParser.PackageParserException e) { return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index eaf6198b67ad3..085681d412e9d 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -29,6 +29,7 @@ import android.compat.annotation.Disabled; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; @@ -73,12 +74,29 @@ public class Environment { private static final String DIR_FILES = "files"; private static final String DIR_CACHE = "cache"; + /** + * The folder name prefix for the user credential protected data directory. This is exposed for + * use in string path caching for {@link ApplicationInfo} objects, and should not be accessed + * directly otherwise. Prefer {@link #getDataUserCeDirectory(String, int)}. + * {@hide} + */ + public static final String DIR_USER_CE = "user"; + + /** + * The folder name prefix for the user device protected data directory. This is exposed for use + * in string path caching for {@link ApplicationInfo} objects, and should not be accessed + * directly otherwise. Prefer {@link #getDataUserDeDirectory(String, int)}. + * {@hide} + */ + public static final String DIR_USER_DE = "user_de"; + /** {@hide} */ @Deprecated public static final String DIRECTORY_ANDROID = DIR_ANDROID; private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system"); - private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data"); + private static final String DIR_ANDROID_DATA_PATH = getDirectoryPath(ENV_ANDROID_DATA, "/data"); + private static final File DIR_ANDROID_DATA = new File(DIR_ANDROID_DATA_PATH); private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand"); private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage"); private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache"); @@ -357,6 +375,14 @@ public class Environment { return DIR_ANDROID_DATA; } + /** + * @see #getDataDirectory() + * @hide + */ + public static String getDataDirectoryPath() { + return DIR_ANDROID_DATA_PATH; + } + /** {@hide} */ public static File getDataDirectory(String volumeUuid) { if (TextUtils.isEmpty(volumeUuid)) { @@ -366,6 +392,15 @@ public class Environment { } } + /** @hide */ + public static String getDataDirectoryPath(String volumeUuid) { + if (TextUtils.isEmpty(volumeUuid)) { + return DIR_ANDROID_DATA_PATH; + } else { + return getExpandDirectory().getAbsolutePath() + File.separator + volumeUuid; + } + } + /** {@hide} */ public static File getExpandDirectory() { return DIR_ANDROID_EXPAND; @@ -489,7 +524,7 @@ public class Environment { /** {@hide} */ public static File getDataUserCeDirectory(String volumeUuid) { - return new File(getDataDirectory(volumeUuid), "user"); + return new File(getDataDirectory(volumeUuid), DIR_USER_CE); } /** {@hide} */ @@ -506,7 +541,7 @@ public class Environment { /** {@hide} */ public static File getDataUserDeDirectory(String volumeUuid) { - return new File(getDataDirectory(volumeUuid), "user_de"); + return new File(getDataDirectory(volumeUuid), DIR_USER_DE); } /** {@hide} */ @@ -1372,6 +1407,12 @@ public class Environment { return path == null ? new File(defaultPath) : new File(path); } + @NonNull + static String getDirectoryPath(@NonNull String variableName, @NonNull String defaultPath) { + String path = System.getenv(variableName); + return path == null ? defaultPath : path; + } + /** {@hide} */ public static void setUserRequired(boolean userRequired) { sUserRequired = userRequired; diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING index 39c196d1a06c6..1bdc82a82c6ce 100644 --- a/core/java/android/os/TEST_MAPPING +++ b/core/java/android/os/TEST_MAPPING @@ -29,6 +29,15 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "file_patterns": ["Environment\\.java"], + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest" + } + ] } ], "postsubmit": [ diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5850dc012226c..35648dabb86db 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9531,10 +9531,16 @@ public class PackageManagerService extends IPackageManager.Stub } } + // The version of the application on the /system partition is less than or + // equal to the version on the /data partition. Throw an exception and use + // the application already installed on the /data partition. if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { - // The version of the application on the /system partition is less than or - // equal to the version on the /data partition. Throw an exception and use - // the application already installed on the /data partition. + // In the case of a skipped package, commitReconciledScanResultLocked is not called to + // add the object to the "live" data structures, so this is the final mutation step + // for the package. Which means it needs to be finalized here to cache derived fields. + // This is relevant for cases where the disabled system package is used for flags or + // other metadata. + ((ParsedPackage) parsedPackage).hideAsFinal(); throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName() + " at " + parsedPackage.getPath() + " ignored: updated version " + pkgSetting.versionCode + " better than this " diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 4476e8ac8d90b..276f88082df07 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -42,7 +42,9 @@ import java.util.Set; * Settings data for a particular package we know about. */ public class PackageSetting extends PackageSettingBase { - int appId; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public int appId; /** * This can be null whenever a physical APK on device is missing. This can be the result of diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index d695a01109ff5..2d2e72a3facb6 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -19,6 +19,7 @@ package com.android.server.pm.parsing; import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.apex.ApexInfo; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -56,6 +57,7 @@ import com.android.internal.util.ArrayUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.PackageStateUnserialized; import libcore.util.EmptyArray; @@ -218,7 +220,9 @@ public class PackageInfoUtils { } ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(pkg, - flags, state, userId); + flags, state, userId, false /* assignUserFields */); + + initForUser(info, pkg, userId); if (pkgSetting != null) { // TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up @@ -349,7 +353,11 @@ public class PackageInfoUtils { if (i == null) return null; InstrumentationInfo info = - PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId); + PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId, + false /* assignUserFields */); + + initForUser(info, pkg, userId); + if (info == null) { return null; } @@ -496,6 +504,90 @@ public class PackageInfoUtils { // @formatter:on } + private static void initForUser(ApplicationInfo output, AndroidPackage input, + @UserIdInt int userId) { + PackageImpl pkg = ((PackageImpl) input); + String packageName = input.getPackageName(); + output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid())); + + if ("android".equals(packageName)) { + output.dataDir = PackageInfoWithoutStateUtils.SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + if (userId == UserHandle.USER_SYSTEM) { + output.credentialProtectedDataDir = + pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName; + output.deviceProtectedDataDir = + pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName; + } else { + // Convert /data/user/0/ -> /data/user/1/com.example.app + String userIdString = String.valueOf(userId); + int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length(); + output.credentialProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser()) + .replace(credentialLength - 2, credentialLength - 1, userIdString) + .append(packageName) + .toString(); + int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length(); + output.deviceProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser()) + .replace(deviceLength - 2, deviceLength - 1, userIdString) + .append(packageName) + .toString(); + } + + if (input.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + output.dataDir = output.deviceProtectedDataDir; + } else { + output.dataDir = output.credentialProtectedDataDir; + } + } + + // This duplicates the ApplicationInfo variant because it uses field assignment and the classes + // don't inherit from each other, unfortunately. Consolidating logic would introduce overhead. + private static void initForUser(InstrumentationInfo output, AndroidPackage input, + @UserIdInt int userId) { + PackageImpl pkg = ((PackageImpl) input); + String packageName = input.getPackageName(); + if ("android".equals(packageName)) { + output.dataDir = PackageInfoWithoutStateUtils.SYSTEM_DATA_PATH; + return; + } + + // For performance reasons, all these paths are built as strings + if (userId == UserHandle.USER_SYSTEM) { + output.credentialProtectedDataDir = + pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName; + output.deviceProtectedDataDir = + pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName; + } else { + // Convert /data/user/0/ -> /data/user/1/com.example.app + String userIdString = String.valueOf(userId); + int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length(); + output.credentialProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser()) + .replace(credentialLength - 2, credentialLength - 1, userIdString) + .append(packageName) + .toString(); + int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length(); + output.deviceProtectedDataDir = + new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser()) + .replace(deviceLength - 2, deviceLength - 1, userIdString) + .append(packageName) + .toString(); + } + + if (input.isDefaultToDeviceProtectedStorage() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + output.dataDir = output.deviceProtectedDataDir; + } else { + output.dataDir = output.credentialProtectedDataDir; + } + } + /** * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache. */ diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java index 0e3e110bdc567..b4c6e9d997516 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java @@ -16,6 +16,7 @@ package com.android.server.pm.parsing.pkg; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; @@ -28,7 +29,9 @@ import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; import android.content.res.TypedArray; +import android.os.Environment; import android.os.Parcel; +import android.os.UserHandle; import android.os.storage.StorageManager; import android.text.TextUtils; @@ -38,6 +41,7 @@ import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; import com.android.server.pm.parsing.PackageInfoUtils; +import java.io.File; import java.util.UUID; /** @@ -87,8 +91,6 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.ParcelWith(ForInternedString.class) private final String manifestPackageName; - private boolean stub; - @Nullable @DataClass.ParcelWith(ForInternedString.class) protected String nativeLibraryDir; @@ -97,8 +99,6 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.ParcelWith(ForInternedString.class) protected String nativeLibraryRootDir; - private boolean nativeLibraryRootRequiresIsa; - @Nullable @DataClass.ParcelWith(ForInternedString.class) protected String primaryCpuAbi; @@ -116,44 +116,102 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.ParcelWith(ForInternedString.class) protected String seInfoUser; - private boolean coreApp; - - private boolean system; - private boolean factoryTest; - - private boolean systemExt; - private boolean privileged; - private boolean oem; - private boolean vendor; - private boolean product; - private boolean odm; - - private boolean signedWithPlatformKey; - /** * This is an appId, the uid if the userId is == USER_SYSTEM */ private int uid = -1; + // This is kept around as a boolean to avoid flag calculation + // during ApplicationInfo generation. + private boolean nativeLibraryRootRequiresIsa; + + private int mBooleans; + + /** + * @see ParsingPackageImpl.Booleans + */ + private static class Booleans { + @IntDef({ + CORE_APP, + SYSTEM, + FACTORY_TEST, + SYSTEM_EXT, + PRIVILEGED, + OEM, + VENDOR, + PRODUCT, + ODM, + SIGNED_WITH_PLATFORM_KEY, + NATIVE_LIBRARY_ROOT_REQUIRES_ISA, + STUB, + }) + public @interface Flags {} + + private static final int CORE_APP = 1; + private static final int SYSTEM = 1 << 1; + private static final int FACTORY_TEST = 1 << 2; + private static final int SYSTEM_EXT = 1 << 3; + private static final int PRIVILEGED = 1 << 4; + private static final int OEM = 1 << 5; + private static final int VENDOR = 1 << 6; + private static final int PRODUCT = 1 << 7; + private static final int ODM = 1 << 8; + private static final int SIGNED_WITH_PLATFORM_KEY = 1 << 9; + private static final int NATIVE_LIBRARY_ROOT_REQUIRES_ISA = 1 << 10; + private static final int STUB = 1 << 11; + } + + private ParsedPackage setBoolean(@Booleans.Flags int flag, boolean value) { + if (value) { + mBooleans |= flag; + } else { + mBooleans &= ~flag; + } + return this; + } + + private boolean getBoolean(@Booleans.Flags int flag) { + return (mBooleans & flag) != 0; + } + + // Derived fields + private int mBaseAppInfoFlags; + private int mBaseAppInfoPrivateFlags; + private String mBaseAppDataCredentialProtectedDirForSystemUser; + private String mBaseAppDataDeviceProtectedDirForSystemUser; + @VisibleForTesting public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath, @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) { super(packageName, baseApkPath, path, manifestArray); this.manifestPackageName = this.packageName; - this.coreApp = isCoreApp; + setBoolean(Booleans.CORE_APP, isCoreApp); } @Override public ParsedPackage hideAsParsed() { + super.hideAsParsed(); return this; } @Override public AndroidPackage hideAsFinal() { // TODO(b/135203078): Lock as immutable + assignDerivedFields(); return this; } + private void assignDerivedFields() { + mBaseAppInfoFlags = PackageInfoUtils.appInfoFlags(this, null); + mBaseAppInfoPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null); + String baseAppDataDir = Environment.getDataDirectoryPath(getVolumeUuid()) + File.separator; + String systemUserSuffix = File.separator + UserHandle.USER_SYSTEM + File.separator; + mBaseAppDataCredentialProtectedDirForSystemUser = TextUtils.safeIntern( + baseAppDataDir + Environment.DIR_USER_CE + systemUserSuffix); + mBaseAppDataDeviceProtectedDirForSystemUser = TextUtils.safeIntern( + baseAppDataDir + Environment.DIR_USER_DE + systemUserSuffix); + } + @Override public long getLongVersionCode() { return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); @@ -437,8 +495,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public ParsedPackage setCoreApp(boolean coreApp) { - this.coreApp = coreApp; - return this; + return setBoolean(Booleans.CORE_APP, coreApp); } @Override @@ -456,8 +513,8 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public ApplicationInfo toAppInfoWithoutState() { ApplicationInfo appInfo = super.toAppInfoWithoutStateWithoutFlags(); - appInfo.flags = PackageInfoUtils.appInfoFlags(this, null); - appInfo.privateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null); + appInfo.flags = mBaseAppInfoFlags; + appInfo.privateFlags = mBaseAppInfoPrivateFlags; appInfo.nativeLibraryDir = nativeLibraryDir; appInfo.nativeLibraryRootDir = nativeLibraryRootDir; appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; @@ -479,7 +536,6 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); sForInternedString.parcel(this.manifestPackageName, dest, flags); - dest.writeBoolean(this.stub); dest.writeString(this.nativeLibraryDir); dest.writeString(this.nativeLibraryRootDir); dest.writeBoolean(this.nativeLibraryRootRequiresIsa); @@ -489,22 +545,12 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka dest.writeString(this.seInfo); dest.writeString(this.seInfoUser); dest.writeInt(this.uid); - dest.writeBoolean(this.coreApp); - dest.writeBoolean(this.system); - dest.writeBoolean(this.factoryTest); - dest.writeBoolean(this.systemExt); - dest.writeBoolean(this.privileged); - dest.writeBoolean(this.oem); - dest.writeBoolean(this.vendor); - dest.writeBoolean(this.product); - dest.writeBoolean(this.odm); - dest.writeBoolean(this.signedWithPlatformKey); + dest.writeInt(this.mBooleans); } public PackageImpl(Parcel in) { super(in); this.manifestPackageName = sForInternedString.unparcel(in); - this.stub = in.readBoolean(); this.nativeLibraryDir = in.readString(); this.nativeLibraryRootDir = in.readString(); this.nativeLibraryRootRequiresIsa = in.readBoolean(); @@ -514,16 +560,9 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka this.seInfo = in.readString(); this.seInfoUser = in.readString(); this.uid = in.readInt(); - this.coreApp = in.readBoolean(); - this.system = in.readBoolean(); - this.factoryTest = in.readBoolean(); - this.systemExt = in.readBoolean(); - this.privileged = in.readBoolean(); - this.oem = in.readBoolean(); - this.vendor = in.readBoolean(); - this.product = in.readBoolean(); - this.odm = in.readBoolean(); - this.signedWithPlatformKey = in.readBoolean(); + this.mBooleans = in.readInt(); + + assignDerivedFields(); } public static final Creator CREATOR = new Creator() { @@ -544,9 +583,8 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka return manifestPackageName; } - @DataClass.Generated.Member public boolean isStub() { - return stub; + return getBoolean(Booleans.STUB); } @Nullable @@ -598,52 +636,52 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public boolean isCoreApp() { - return coreApp; + return getBoolean(Booleans.CORE_APP); } @Override public boolean isSystem() { - return system; + return getBoolean(Booleans.SYSTEM); } @Override public boolean isFactoryTest() { - return factoryTest; + return getBoolean(Booleans.FACTORY_TEST); } @Override public boolean isSystemExt() { - return systemExt; + return getBoolean(Booleans.SYSTEM_EXT); } @Override public boolean isPrivileged() { - return privileged; + return getBoolean(Booleans.PRIVILEGED); } @Override public boolean isOem() { - return oem; + return getBoolean(Booleans.OEM); } @Override public boolean isVendor() { - return vendor; + return getBoolean(Booleans.VENDOR); } @Override public boolean isProduct() { - return product; + return getBoolean(Booleans.PRODUCT); } @Override public boolean isOdm() { - return odm; + return getBoolean(Booleans.ODM); } @Override public boolean isSignedWithPlatformKey() { - return signedWithPlatformKey; + return getBoolean(Booleans.SIGNED_WITH_PLATFORM_KEY); } /** @@ -656,7 +694,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @DataClass.Generated.Member public PackageImpl setStub(boolean value) { - stub = value; + setBoolean(Booleans.STUB, value); return this; } @@ -668,77 +706,72 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka @Override public PackageImpl setSystem(boolean value) { - system = value; + setBoolean(Booleans.SYSTEM, value); return this; } @Override public PackageImpl setFactoryTest(boolean value) { - factoryTest = value; + setBoolean(Booleans.FACTORY_TEST, value); return this; } @Override public PackageImpl setSystemExt(boolean value) { - systemExt = value; + setBoolean(Booleans.SYSTEM_EXT, value); return this; } @Override public PackageImpl setPrivileged(boolean value) { - privileged = value; + setBoolean(Booleans.PRIVILEGED, value); return this; } @Override public PackageImpl setOem(boolean value) { - oem = value; + setBoolean(Booleans.OEM, value); return this; } @Override public PackageImpl setVendor(boolean value) { - vendor = value; + setBoolean(Booleans.VENDOR, value); return this; } @Override public PackageImpl setProduct(boolean value) { - product = value; + setBoolean(Booleans.PRODUCT, value); return this; } @Override public PackageImpl setOdm(boolean value) { - odm = value; + setBoolean(Booleans.ODM, value); return this; } @Override public PackageImpl setSignedWithPlatformKey(boolean value) { - signedWithPlatformKey = value; + setBoolean(Booleans.SIGNED_WITH_PLATFORM_KEY, value); return this; } - /** - * This is an appId, the uid if the userId is == USER_SYSTEM - */ @Override public PackageImpl setUid(int value) { uid = value; return this; } - @DataClass.Generated( - time = 1580517688900L, - codegenVersion = "1.0.14", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String manifestPackageName\nprivate boolean stub\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryRootDir\nprivate boolean nativeLibraryRootRequiresIsa\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String primaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryNativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfo\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfoUser\nprivate boolean system\nprivate boolean factoryTest\nprivate boolean systemExt\nprivate boolean privileged\nprivate boolean oem\nprivate boolean vendor\nprivate boolean product\nprivate boolean odm\nprivate boolean signedWithPlatformKey\nprivate int uid\npublic static final com.android.server.pm.parsing.pkg.Creator CREATOR\npublic static com.android.server.pm.parsing.pkg.PackageImpl forParsing(java.lang.String,java.lang.String,java.lang.String,android.content.res.TypedArray,boolean)\npublic static com.android.server.pm.parsing.pkg.AndroidPackage buildFakeForDeletion(java.lang.String,java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage hideAsParsed()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.AndroidPackage hideAsFinal()\npublic @java.lang.Override long getLongVersionCode()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removePermission(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesOptionalLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesOptionalLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSigningDetails(android.content.pm.PackageParser.SigningDetails)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRestrictUpdateHash(byte)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRealPackage(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPersistent(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearProtectedBroadcasts()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearOriginalPackages()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearAdoptPermissions()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPackageName(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setAllComponentsDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setBaseCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryRootDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPrimaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfo(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfoUser(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSplitCodePaths(java.lang.String[])\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl capPermissionPriorities()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl markNotActivitiesAsNotExportedIfSingleUser()\npublic @java.lang.Override java.util.UUID getStorageUuid()\npublic @java.lang.Deprecated @java.lang.Override java.lang.String toAppInfoToString()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setCoreApp(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCode(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCodeMajor(int)\npublic @java.lang.Override android.content.pm.ApplicationInfo toAppInfoWithoutState()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass PackageImpl extends android.content.pm.parsing.ParsingPackageImpl implements [com.android.server.pm.parsing.pkg.ParsedPackage, com.android.server.pm.parsing.pkg.AndroidPackage]\n@com.android.internal.util.DataClass(genConstructor=false, genParcelable=false, genAidl=false, genBuilder=false, genHiddenConstructor=false, genCopyConstructor=false)") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code + // The following methods are explicitly not inside any interface. These are hidden under + // PackageImpl which is only accessible to the system server. This is to prevent/discourage + // usage of these fields outside of the utility classes. + public String getBaseAppDataCredentialProtectedDirForSystemUser() { + return mBaseAppDataCredentialProtectedDirForSystemUser; + } + public String getBaseAppDataDeviceProtectedDirForSystemUser() { + return mBaseAppDataDeviceProtectedDirForSystemUser; + } } diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java index 2660f2bda23b0..657f32c25a89b 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java @@ -105,6 +105,9 @@ public interface ParsedPackage extends AndroidPackage { ParsedPackage setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir); + /** + * This is an appId, the uid if the userId is == USER_SYSTEM + */ ParsedPackage setUid(int uid); ParsedPackage setVersionCode(int versionCode); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 1d384e961dc33..dc9d1ac5240d3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -155,8 +155,8 @@ public class PackageParserTest { @Test public void test_serializePackage() throws Exception { try (PackageParser2 pp = PackageParser2.forParsingFileWithDefaults()) { - ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, - true /* useCaches */); + AndroidPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, + true /* useCaches */).hideAsFinal(); Parcel p = Parcel.obtain(); pkg.writeToParcel(p, 0 /* flags */); @@ -591,7 +591,7 @@ public class PackageParserTest { null ) .setUse32BitAbi(true) - .setVolumeUuid("foo3") + .setVolumeUuid("d52ef59a-7def-4541-bf21-4c28ed4b65a0") .addPermission(permission) .addPermissionGroup(new ParsedPermissionGroup()) .addActivity(new ParsedActivity()) diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index 4d8cc46472711..1ca3c7488f328 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -470,11 +470,16 @@ public class ScanTests { private PackageManagerService.ScanResult executeScan( PackageManagerService.ScanRequest scanRequest) throws PackageManagerException { - return PackageManagerService.scanPackageOnlyLI( + PackageManagerService.ScanResult result = PackageManagerService.scanPackageOnlyLI( scanRequest, mMockInjector, false /*isUnderFactoryTest*/, System.currentTimeMillis()); + + // Need to call hideAsFinal to cache derived fields. This is normally done in PMS, but not + // in this cut down flow used for the test. + ((ParsedPackage) result.pkgSetting.pkg).hideAsFinal(); + return result; } private static String createCodePath(String packageName) { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt index d36dcce800ebb..fb82b1d3d3f63 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt @@ -59,7 +59,9 @@ class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() { fun appInfo(flag: Int, fieldFunction: (ApplicationInfo) -> List) = Param( flag, ApplicationInfo::class.java.simpleName, - ::oldAppInfo, ::newAppInfo, fieldFunction + { pkg, flags -> oldAppInfo(pkg, flags) }, + { pkg, flags -> newAppInfo(pkg, flags) }, + fieldFunction ) } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt index 0f5f0af8f8388..61252c4735308 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt @@ -32,6 +32,7 @@ import android.content.pm.ServiceInfo import android.os.Bundle import android.os.Debug import android.os.Environment +import android.os.Process import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.server.pm.PackageManagerService @@ -54,7 +55,7 @@ open class AndroidPackageParsingTestBase { private const val VERIFY_ALL_APKS = true - /** For auditing memory usage differences */ + // For auditing memory usage differences to /sdcard/AndroidPackageParsingTestBase.hprof private const val DUMP_HPROF_TO_EXTERNAL = false val context: Context = InstrumentationRegistry.getInstrumentation().getContext() @@ -104,10 +105,12 @@ open class AndroidPackageParsingTestBase { @JvmStatic @BeforeClass fun setUpPackages() { + var uid = Process.FIRST_APPLICATION_UID apks.mapNotNull { try { packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) to - packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) + packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, + false) } catch (ignored: Exception) { // It is intentional that a failure of either call here will result in failing // both. Having null on one side would mean nothing to compare. Due to the @@ -117,8 +120,15 @@ open class AndroidPackageParsingTestBase { null } }.forEach { (old, new) -> + // Assign an arbitrary UID. This is normally done after parsing completes, inside + // PackageManagerService, but since that code isn't run here, need to mock it. This + // is equivalent to what the system would assign. + old.applicationInfo.uid = uid + new.uid = uid + uid++ + oldPackages += old - newPackages += new + newPackages += new.hideAsFinal() } if (DUMP_HPROF_TO_EXTERNAL) { @@ -131,12 +141,29 @@ open class AndroidPackageParsingTestBase { } } - fun oldAppInfo(pkg: PackageParser.Package, flags: Int = 0): ApplicationInfo? { - return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, 0) + fun oldAppInfo( + pkg: PackageParser.Package, + flags: Int = 0, + userId: Int = 0 + ): ApplicationInfo? { + return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, userId) } - fun newAppInfo(pkg: AndroidPackage, flags: Int = 0): ApplicationInfo? { - return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, 0, + fun newAppInfo( + pkg: AndroidPackage, + flags: Int = 0, + userId: Int = 0 + ): ApplicationInfo? { + return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId, + mockPkgSetting(pkg)) + } + + fun newAppInfoWithoutState( + pkg: AndroidPackage, + flags: Int = 0, + userId: Int = 0 + ): ApplicationInfo? { + return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId, mockPkgSetting(pkg)) } @@ -152,6 +179,7 @@ open class AndroidPackageParsingTestBase { private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked { this.pkg = aPkg + this.appId = aPkg.uid whenever(pkgState) { PackageStateUnserialized() } whenever(readUserState(anyInt())) { dummyUserState } } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt new file mode 100644 index 0000000000000..67b5d683de9a5 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.parsing + +import android.content.pm.ApplicationInfo +import android.content.pm.PackageParser +import android.os.Environment +import android.os.UserHandle +import android.platform.test.annotations.Presubmit +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Test + +/** + * As a performance optimization, the new parsing code builds the user data directories manually + * using string concatenation. This tries to mirror the logic that [Environment] uses, but it is + * still fragile to changes and potentially different device configurations. + * + * This compares the resultant values against the old [PackageParser] outputs as well as + * [ApplicationInfo]'s own [ApplicationInfo.initForUser]. + */ +@Presubmit +class PackageInfoUserFieldsTest : AndroidPackageParsingTestBase() { + + @Test + fun userEnvironmentValues() { + // Specifically use a large user ID to test assumptions about single character IDs + val userId = 110 + + oldPackages.zip(newPackages) + .map { (old, new) -> + (old to oldAppInfo(pkg = old, userId = userId)!!) to + (new to newAppInfo(pkg = new, userId = userId)!!) + } + .forEach { (oldPair, newPair) -> + val (oldPkg, oldInfo) = oldPair + val (newPkg, newInfo) = newPair + + val oldValuesActual = extractActual(oldInfo) + val newValuesActual = extractActual(newInfo) + val oldValuesExpected: Values + val newValuesExpected: Values + + val packageName = oldPkg.packageName + if (packageName == "android") { + val systemDataDir = Environment.getDataSystemDirectory().absolutePath + oldValuesExpected = Values( + uid = UserHandle.getUid(userId, + UserHandle.getAppId(oldPkg.applicationInfo.uid)), + userDe = null, + userCe = null, + dataDir = systemDataDir + ) + newValuesExpected = Values( + uid = UserHandle.getUid(userId, UserHandle.getAppId(newPkg.uid)), + userDe = null, + userCe = null, + dataDir = systemDataDir + ) + } else { + oldValuesExpected = extractExpected(oldInfo, oldInfo.uid, userId) + newValuesExpected = extractExpected(newInfo, newPkg.uid, userId) + } + + // Calls the internal ApplicationInfo logic to compare against. This must be + // done after saving the original values, since this will overwrite them. + oldInfo.initForUser(userId) + newInfo.initForUser(userId) + + val oldInitValues = extractActual(oldInfo) + val newInitValues = extractActual(newInfo) + + // The optimization is also done for the no state API that isn't used by the + // system. This API is still exposed publicly, so for this test we should + // verify it. + val newNoStateValues = extractActual( + newAppInfoWithoutState(newPkg, 0, userId)!!) + + assertAllEquals(packageName, + oldValuesActual, oldValuesExpected, oldInitValues, + newValuesActual, newValuesExpected, newInitValues, newNoStateValues) + } + } + + private fun assertAllEquals(packageName: String, vararg values: Values) { + // Local function to avoid accidentally calling wrong type + fun assertAllEquals(message: String, vararg values: Any?) { + values.forEachIndexed { index, value -> + if (index == 0) return@forEachIndexed + assertWithMessage("$message $index").that(values[0]).isEqualTo(value) + } + } + + assertAllEquals("$packageName mismatched uid", values.map { it.uid }) + assertAllEquals("$packageName mismatched userDe", values.map { it.userDe }) + assertAllEquals("$packageName mismatched userCe", values.map { it.userCe }) + assertAllEquals("$packageName mismatched dataDir", values.map { it.dataDir }) + } + + private fun extractActual(appInfo: ApplicationInfo) = Values( + uid = appInfo.uid, + userDe = appInfo.deviceProtectedDataDir, + userCe = appInfo.credentialProtectedDataDir, + dataDir = appInfo.dataDir + ) + + private fun extractExpected(appInfo: ApplicationInfo, appIdUid: Int, userId: Int): Values { + val userDe = Environment.getDataUserDePackageDirectory(appInfo.volumeUuid, userId, + appInfo.packageName).absolutePath + val userCe = Environment.getDataUserCePackageDirectory(appInfo.volumeUuid, userId, + appInfo.packageName).absolutePath + val dataDir = if (appInfo.isDefaultToDeviceProtectedStorage) { + appInfo.deviceProtectedDataDir + } else { + appInfo.credentialProtectedDataDir + } + + return Values( + uid = UserHandle.getUid(userId, UserHandle.getAppId(appIdUid)), + userDe = userDe, + userCe = userCe, + dataDir = dataDir + ) + } + + data class Values( + val uid: Int, + val userDe: String?, + val userCe: String?, + val dataDir: String? + ) +}