diff options
| author | Winson Chiu <chiuwinson@google.com> | 2019-12-09 21:46:13 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-12-09 21:46:13 +0000 |
| commit | f0ff68f6250f6e4e0bcb24fbae1108e2caba570f (patch) | |
| tree | 11b0944b867429221d21762c3d70024f42035b59 /core/java/android | |
| parent | 838c56cf6751e64029072d6f36d3bb7b815e7344 (diff) | |
| parent | 6571c8a46154b800d16a2fe850c4b2129b209a2a (diff) | |
Merge changes from topic "androidpackage"
* changes:
Critical fixes for AndroidPackage migration
Revert Revert PackageParser#Package fixes
Revert "Revert "Deprecate PackageParser#Package""
Diffstat (limited to 'core/java/android')
22 files changed, 12090 insertions, 311 deletions
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index e72444399dd0..26193f6ebb3d 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1376,7 +1376,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { this.minHeight = minHeight; } - WindowLayout(Parcel source) { + /** @hide */ + public WindowLayout(Parcel source) { width = source.readInt(); widthFraction = source.readFloat(); height = source.readInt(); diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index d6fb28f694fd..aa0002df2c3a 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -373,8 +373,9 @@ public class PackageInfo implements Parcelable { /** * Whether the overlay is static, meaning it cannot be enabled/disabled at runtime. + * @hide */ - boolean mOverlayIsStatic; + public boolean mOverlayIsStatic; /** * The user-visible SDK version (ex. 26) of the framework against which the application claims diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index cf21e9665c71..65ee1e5f0370 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -57,6 +57,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageParserCacheHelper.ReadHelper; import android.content.pm.PackageParserCacheHelper.WriteHelper; +import android.content.pm.parsing.AndroidPackage; +import android.content.pm.parsing.ApkParseUtils; +import android.content.pm.parsing.ComponentParseUtils; +import android.content.pm.parsing.PackageImpl; +import android.content.pm.parsing.PackageInfoUtils; +import android.content.pm.parsing.ParsedPackage; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; @@ -67,7 +73,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.FileUtils; @@ -156,21 +161,22 @@ import java.util.concurrent.atomic.AtomicInteger; * @hide */ public class PackageParser { - private static final boolean DEBUG_JAR = false; - private static final boolean DEBUG_PARSER = false; - private static final boolean DEBUG_BACKUP = false; - private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE; - private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100; + + public static final boolean DEBUG_JAR = false; + public static final boolean DEBUG_PARSER = false; + public static final boolean DEBUG_BACKUP = false; + public static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE; + public static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100; private static final String PROPERTY_CHILD_PACKAGES_ENABLED = "persist.sys.child_packages_enabled"; - private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE && + public static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false); - private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f; - private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f; - private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f; + public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f; + public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f; + public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f; private static final int DEFAULT_MIN_SDK_VERSION = 1; private static final int DEFAULT_TARGET_SDK_VERSION = 0; @@ -182,37 +188,38 @@ public class PackageParser { public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; /** Path prefix for apps on expanded storage */ - private static final String MNT_EXPAND = "/mnt/expand/"; - - private static final String TAG_MANIFEST = "manifest"; - private static final String TAG_APPLICATION = "application"; - private static final String TAG_PACKAGE_VERIFIER = "package-verifier"; - private static final String TAG_OVERLAY = "overlay"; - private static final String TAG_KEY_SETS = "key-sets"; - private static final String TAG_PERMISSION_GROUP = "permission-group"; - private static final String TAG_PERMISSION = "permission"; - private static final String TAG_PERMISSION_TREE = "permission-tree"; - private static final String TAG_USES_PERMISSION = "uses-permission"; - private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; - private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23"; - private static final String TAG_USES_CONFIGURATION = "uses-configuration"; - private static final String TAG_USES_FEATURE = "uses-feature"; - private static final String TAG_FEATURE_GROUP = "feature-group"; - private static final String TAG_USES_SDK = "uses-sdk"; - private static final String TAG_SUPPORT_SCREENS = "supports-screens"; - private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; - private static final String TAG_INSTRUMENTATION = "instrumentation"; - private static final String TAG_ORIGINAL_PACKAGE = "original-package"; - private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; - private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; - private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; - private static final String TAG_SUPPORTS_INPUT = "supports-input"; - private static final String TAG_EAT_COMMENT = "eat-comment"; - private static final String TAG_PACKAGE = "package"; - private static final String TAG_RESTRICT_UPDATE = "restrict-update"; - private static final String TAG_USES_SPLIT = "uses-split"; - - private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; + public static final String MNT_EXPAND = "/mnt/expand/"; + + public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; + public static final String TAG_APPLICATION = "application"; + public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; + public static final String TAG_EAT_COMMENT = "eat-comment"; + public static final String TAG_FEATURE_GROUP = "feature-group"; + public static final String TAG_INSTRUMENTATION = "instrumentation"; + public static final String TAG_KEY_SETS = "key-sets"; + public static final String TAG_MANIFEST = "manifest"; + public static final String TAG_ORIGINAL_PACKAGE = "original-package"; + public static final String TAG_OVERLAY = "overlay"; + public static final String TAG_PACKAGE = "package"; + public static final String TAG_PACKAGE_VERIFIER = "package-verifier"; + public static final String TAG_PERMISSION = "permission"; + public static final String TAG_PERMISSION_GROUP = "permission-group"; + public static final String TAG_PERMISSION_TREE = "permission-tree"; + public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; + public static final String TAG_QUERIES = "queries"; + public static final String TAG_RESTRICT_UPDATE = "restrict-update"; + public static final String TAG_SUPPORT_SCREENS = "supports-screens"; + public static final String TAG_SUPPORTS_INPUT = "supports-input"; + public static final String TAG_USES_CONFIGURATION = "uses-configuration"; + public static final String TAG_USES_FEATURE = "uses-feature"; + public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; + public static final String TAG_USES_PERMISSION = "uses-permission"; + public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23"; + public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; + public static final String TAG_USES_SDK = "uses-sdk"; + public static final String TAG_USES_SPLIT = "uses-split"; + + public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; /** * Bit mask of all the valid bits that can be set in recreateOnConfigChanges. @@ -222,25 +229,25 @@ public class PackageParser { ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC; // These are the tags supported by child packages - private static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>(); + public static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>(); static { CHILD_PACKAGE_TAGS.add(TAG_APPLICATION); - CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION); - CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M); - CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23); - CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION); - CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE); + CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS); + CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT); CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP); - CHILD_PACKAGE_TAGS.add(TAG_USES_SDK); - CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS); CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION); - CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE); - CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS); + CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS); CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT); - CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT); + CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION); + CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE); + CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE); + CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION); + CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23); + CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M); + CHILD_PACKAGE_TAGS.add(TAG_USES_SDK); } - private static final boolean LOG_UNSAFE_BROADCASTS = false; + public static final boolean LOG_UNSAFE_BROADCASTS = false; /** * Total number of packages that were read from the cache. We use it only for logging. @@ -248,7 +255,7 @@ public class PackageParser { public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger(); // Set of broadcast actions that are safe for manifest receivers - private static final Set<String> SAFE_BROADCASTS = new ArraySet<>(); + public static final Set<String> SAFE_BROADCASTS = new ArraySet<>(); static { SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED); } @@ -295,26 +302,29 @@ public class PackageParser { * @deprecated callers should move to explicitly passing around source path. */ @Deprecated - private String mArchiveSourcePath; + public String mArchiveSourcePath; - private String[] mSeparateProcesses; + public String[] mSeparateProcesses; private boolean mOnlyCoreApps; private DisplayMetrics mMetrics; @UnsupportedAppUsage - private Callback mCallback; + public Callback mCallback; private File mCacheDir; - private static final int SDK_VERSION = Build.VERSION.SDK_INT; - private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; + public static final int SDK_VERSION = Build.VERSION.SDK_INT; + public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; + + public int mParseError = PackageManager.INSTALL_SUCCEEDED; - private int mParseError = PackageManager.INSTALL_SUCCEEDED; + public ThreadLocal<ApkParseUtils.ParseResult> mSharedResult + = ThreadLocal.withInitial(ApkParseUtils.ParseResult::new); - private static boolean sCompatibilityModeEnabled = true; - private static boolean sUseRoundIcon = false; + public static boolean sCompatibilityModeEnabled = true; + public static boolean sUseRoundIcon = false; - private static final int PARSE_DEFAULT_INSTALL_LOCATION = + public static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; - private static final int PARSE_DEFAULT_TARGET_SANDBOX = 1; + public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1; static class ParsePackageItemArgs { final Package owner; @@ -536,7 +546,7 @@ public class PackageParser { * the DTD. Otherwise, we try to get as much from the package as we * can without failing. This should normally be set to false, to * support extensions to the DTD in future versions. */ - private static final boolean RIGID_PARSER = false; + public static final boolean RIGID_PARSER = false; private static final String TAG = "PackageParser"; @@ -887,7 +897,7 @@ public class PackageParser { @Retention(RetentionPolicy.SOURCE) public @interface ParseFlags {} - private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); + public static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); /** * Used to sort a set of APKs based on their split names, always placing the @@ -1033,7 +1043,7 @@ public class PackageParser { * and unique split names. * <p> * Note that this <em>does not</em> perform signature verification; that - * must be done separately in {@link #collectCertificates(Package, int)}. + * must be done separately in {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}. * * If {@code useCaches} is true, the package parser might return a cached * result from a previous parse of the same {@code packageFile} with the same @@ -1041,21 +1051,54 @@ public class PackageParser { * has changed since the last parse, it's up to callers to do so. * * @see #parsePackageLite(File, int) + * @deprecated use {@link #parseParsedPackage(File, int, boolean)} */ @UnsupportedAppUsage + @Deprecated public Package parsePackage(File packageFile, int flags, boolean useCaches) throws PackageParserException { - Package parsed = useCaches ? getCachedResult(packageFile, flags) : null; + if (packageFile.isDirectory()) { + return parseClusterPackage(packageFile, flags); + } else { + return parseMonolithicPackage(packageFile, flags); + } + } + + /** + * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}. + * @deprecated use {@link #parseParsedPackage(File, int, boolean)} + */ + @UnsupportedAppUsage + @Deprecated + public Package parsePackage(File packageFile, int flags) throws PackageParserException { + return parsePackage(packageFile, flags, false /* useCaches */); + } + + /** + * Updated method which returns {@link ParsedPackage}, the current representation of a + * package parsed from disk. + * + * @see #parsePackage(File, int, boolean) + */ + public ParsedPackage parseParsedPackage(File packageFile, int flags, boolean useCaches) + throws PackageParserException { + ParsedPackage parsed = useCaches ? getCachedResult(packageFile, flags) : null; if (parsed != null) { return parsed; } long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0; - if (packageFile.isDirectory()) { - parsed = parseClusterPackage(packageFile, flags); - } else { - parsed = parseMonolithicPackage(packageFile, flags); - } + ApkParseUtils.ParseInput parseInput = mSharedResult.get().reset(); + parsed = ApkParseUtils.parsePackage( + parseInput, + mSeparateProcesses, + mCallback, + mMetrics, + mOnlyCoreApps, + packageFile, + flags + ) + .hideAsParsed(); long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0; cacheResult(packageFile, flags, parsed); @@ -1067,19 +1110,12 @@ public class PackageParser { + "ms, update_cache=" + cacheTime + " ms"); } } - return parsed; - } - /** - * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}. - */ - @UnsupportedAppUsage - public Package parsePackage(File packageFile, int flags) throws PackageParserException { - return parsePackage(packageFile, flags, false /* useCaches */); + return parsed; } /** - * Returns the cache key for a specificied {@code packageFile} and {@code flags}. + * Returns the cache key for a specified {@code packageFile} and {@code flags}. */ private String getCacheKey(File packageFile, int flags) { StringBuilder sb = new StringBuilder(packageFile.getName()); @@ -1090,13 +1126,13 @@ public class PackageParser { } @VisibleForTesting - protected Package fromCacheEntry(byte[] bytes) { + protected ParsedPackage fromCacheEntry(byte[] bytes) { return fromCacheEntryStatic(bytes); } /** static version of {@link #fromCacheEntry} for unit tests. */ @VisibleForTesting - public static Package fromCacheEntryStatic(byte[] bytes) { + public static ParsedPackage fromCacheEntryStatic(byte[] bytes) { final Parcel p = Parcel.obtain(); p.unmarshall(bytes, 0, bytes.length); p.setDataPosition(0); @@ -1104,7 +1140,8 @@ public class PackageParser { final ReadHelper helper = new ReadHelper(p); helper.startAndInstall(); - PackageParser.Package pkg = new PackageParser.Package(p); + // TODO(b/135203078): Hide PackageImpl constructor? + ParsedPackage pkg = new PackageImpl(p); p.recycle(); @@ -1114,14 +1151,14 @@ public class PackageParser { } @VisibleForTesting - protected byte[] toCacheEntry(Package pkg) { + protected byte[] toCacheEntry(ParsedPackage pkg) { return toCacheEntryStatic(pkg); } /** static version of {@link #toCacheEntry} for unit tests. */ @VisibleForTesting - public static byte[] toCacheEntryStatic(Package pkg) { + public static byte[] toCacheEntryStatic(ParsedPackage pkg) { final Parcel p = Parcel.obtain(); final WriteHelper helper = new WriteHelper(p); @@ -1170,7 +1207,7 @@ public class PackageParser { * Returns the cached parse result for {@code packageFile} for parse flags {@code flags}, * or {@code null} if no cached result exists. */ - private Package getCachedResult(File packageFile, int flags) { + public ParsedPackage getCachedResult(File packageFile, int flags) { if (mCacheDir == null) { return null; } @@ -1199,7 +1236,7 @@ public class PackageParser { /** * Caches the parse result for {@code packageFile} with flags {@code flags}. */ - private void cacheResult(File packageFile, int flags, Package parsed) { + public void cacheResult(File packageFile, int flags, ParsedPackage parsed) { if (mCacheDir == null) { return; } @@ -1238,7 +1275,8 @@ public class PackageParser { * split names. * <p> * Note that this <em>does not</em> perform signature verification; that - * must be done separately in {@link #collectCertificates(Package, int)}. + * must be done separately in + * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}. */ private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException { final PackageLite lite = parseClusterPackageLite(packageDir, 0); @@ -1302,10 +1340,11 @@ public class PackageParser { * Parse the given APK file, treating it as as a single monolithic package. * <p> * Note that this <em>does not</em> perform signature verification; that - * must be done separately in {@link #collectCertificates(Package, int)}. + * must be done separately in + * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}. * * @deprecated external callers should move to - * {@link #parsePackage(File, int)}. Eventually this method will + * {@link #parseParsedPackage(File, int, boolean)}. Eventually this method will * be marked private. */ @Deprecated @@ -1505,8 +1544,11 @@ public class PackageParser { * Collect certificates from all the APKs described in the given package, * populating {@link Package#mSigningDetails}. Also asserts that all APK * contents are signed correctly and consistently. + * + * @deprecated use {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)} */ @UnsupportedAppUsage + @Deprecated public static void collectCertificates(Package pkg, boolean skipVerify) throws PackageParserException { collectCertificatesInternal(pkg, skipVerify); @@ -1685,7 +1727,7 @@ public class PackageParser { ? null : "must have at least one '.' separator"; } - private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser, + public static Pair<String, String> parsePackageSplitNames(XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException, PackageParserException { @@ -2454,8 +2496,6 @@ public class PackageParser { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; - } else if (tagName.equals("queries")) { - parseQueries(pkg, res, parser, flags, outError); } else { Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName() + " at " + mArchiveSourcePath + " " @@ -2925,7 +2965,7 @@ public class PackageParser { return true; } - private static String buildClassName(String pkg, CharSequence clsSeq, + public static String buildClassName(String pkg, CharSequence clsSeq, String[] outError) { if (clsSeq == null || clsSeq.length() <= 0) { outError[0] = "Empty class name in package " + pkg; @@ -2973,7 +3013,7 @@ public class PackageParser { return proc; } - private static String buildProcessName(String pkg, String defProc, + public static String buildProcessName(String pkg, String defProc, CharSequence procSeq, int flags, String[] separateProcesses, String[] outError) { if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) { @@ -2993,7 +3033,7 @@ public class PackageParser { return TextUtils.safeIntern(buildCompoundName(pkg, procSeq, "process", outError)); } - private static String buildTaskAffinityName(String pkg, String defProc, + public static String buildTaskAffinityName(String pkg, String defProc, CharSequence procSeq, String[] outError) { if (procSeq == null) { return defProc; @@ -3555,9 +3595,6 @@ public class PackageParser { owner.mRequiredAccountType = requiredAccountType; } - owner.mForceQueryable = - sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false); - if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_debuggable, false)) { @@ -4019,89 +4056,6 @@ public class PackageParser { return true; } - private boolean parseQueries(Package owner, Resources res, XmlResourceParser parser, int flags, - String[] outError) - throws IOException, XmlPullParserException { - - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - if (parser.getName().equals("intent")) { - QueriesIntentInfo intentInfo = new QueriesIntentInfo(); - if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, - intentInfo, outError)) { - return false; - } - - Uri data = null; - String dataType = null; - String host = ""; - final int numActions = intentInfo.countActions(); - final int numSchemes = intentInfo.countDataSchemes(); - final int numTypes = intentInfo.countDataTypes(); - final int numHosts = intentInfo.getHosts().length; - if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) { - outError[0] = "intent tags must contain either an action or data."; - return false; - } - if (numActions > 1) { - outError[0] = "intent tag may have at most one action."; - return false; - } - if (numTypes > 1) { - outError[0] = "intent tag may have at most one data type."; - return false; - } - if (numSchemes > 1) { - outError[0] = "intent tag may have at most one data scheme."; - return false; - } - if (numHosts > 1) { - outError[0] = "intent tag may have at most one data host."; - return false; - } - Intent intent = new Intent(); - for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { - intent.addCategory(intentInfo.getCategory(i)); - } - if (numHosts == 1) { - host = intentInfo.getHosts()[0]; - } - if (numSchemes == 1) { - data = new Uri.Builder() - .scheme(intentInfo.getDataScheme(0)) - .authority(host) - .build(); - } - if (numTypes == 1) { - dataType = intentInfo.getDataType(0); - } - intent.setDataAndType(data, dataType); - if (numActions == 1) { - intent.setAction(intentInfo.getAction(0)); - } - owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent); - } else if (parser.getName().equals("package")) { - final TypedArray sa = res.obtainAttributes(parser, - com.android.internal.R.styleable.AndroidManifestQueriesPackage); - final String packageName = - sa.getString(R.styleable.AndroidManifestQueriesPackage_name); - if (TextUtils.isEmpty(packageName)) { - outError[0] = "Package name is missing from package tag."; - return false; - } - owner.mQueriesPackages = - ArrayUtils.add(owner.mQueriesPackages, packageName.intern()); - } - } - return true; - } - /** * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI */ @@ -5813,7 +5767,7 @@ public class PackageParser { return null; } - private static final String ANDROID_RESOURCES + public static final String ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android"; private boolean parseIntent(Resources res, XmlResourceParser parser, boolean allowGlobs, @@ -6508,7 +6462,10 @@ public class PackageParser { /** * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. + * + * @deprecated use an {@link AndroidPackage} */ + @Deprecated public final static class Package implements Parcelable { @UnsupportedAppUsage @@ -6616,9 +6573,6 @@ public class PackageParser { // The major version code declared for this package. public int mVersionCodeMajor; - // Whether the package declares that it should be queryable by all normal apps on device. - public boolean mForceQueryable; - // Return long containing mVersionCode and mVersionCodeMajor. public long getLongVersionCode() { return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode); @@ -6724,9 +6678,6 @@ public class PackageParser { /** Whether or not the package is a stub and must be replaced by the full version. */ public boolean isStub; - public ArrayList<String> mQueriesPackages; - public ArrayList<Intent> mQueriesIntents; - @UnsupportedAppUsage public Package(String packageName) { this.packageName = packageName; @@ -7230,9 +7181,6 @@ public class PackageParser { use32bitAbi = (dest.readInt() == 1); restrictUpdateHash = dest.createByteArray(); visibleToInstantApps = dest.readInt() == 1; - mForceQueryable = dest.readBoolean(); - mQueriesIntents = dest.createTypedArrayList(Intent.CREATOR); - mQueriesPackages = dest.createStringArrayList(); } private static void internStringArrayList(List<String> list) { @@ -7248,7 +7196,7 @@ public class PackageParser { * Sets the package owner and the the {@code applicationInfo} for every component * owner by this package. */ - private void fixupOwner(List<? extends Component<?>> list) { + public void fixupOwner(List<? extends Component<?>> list) { if (list != null) { for (Component<?> c : list) { c.owner = this; @@ -7358,12 +7306,8 @@ public class PackageParser { dest.writeInt(use32bitAbi ? 1 : 0); dest.writeByteArray(restrictUpdateHash); dest.writeInt(visibleToInstantApps ? 1 : 0); - dest.writeBoolean(mForceQueryable); - dest.writeTypedList(mQueriesIntents); - dest.writeList(mQueriesPackages); } - /** * Writes the keyset mapping to the provided package. {@code null} mappings are permitted. */ @@ -7435,6 +7379,10 @@ public class PackageParser { }; } + /** + * @deprecated use a {@link ComponentParseUtils.ParsedComponent} + */ + @Deprecated public static abstract class Component<II extends IntentInfo> { @UnsupportedAppUsage public final ArrayList<II> intents; @@ -7615,6 +7563,10 @@ public class PackageParser { } } + /** + * @deprecated use {@link ComponentParseUtils.ParsedPermission} + */ + @Deprecated public final static class Permission extends Component<IntentInfo> implements Parcelable { @UnsupportedAppUsage public final PermissionInfo info; @@ -7689,6 +7641,10 @@ public class PackageParser { }; } + /** + * @deprecated use {@link ComponentParseUtils.ParsedPermissionGroup} + */ + @Deprecated public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable { @UnsupportedAppUsage public final PermissionGroupInfo info; @@ -7788,7 +7744,12 @@ public class PackageParser { return false; } + /** + * @deprecated use {@link PackageInfoUtils#generateApplicationInfo( + * AndroidPackage, int, PackageUserState, int)} + */ @UnsupportedAppUsage + @Deprecated public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state) { return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId()); @@ -7845,7 +7806,12 @@ public class PackageParser { ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } + /** + * @deprecated use {@link PackageInfoUtils#generateApplicationInfo( + * AndroidPackage, int, PackageUserState, int)} + */ @UnsupportedAppUsage + @Deprecated public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state, int userId) { if (p == null) return null; @@ -7885,6 +7851,11 @@ public class PackageParser { return ai; } + /** + * @deprecated use {@link PackageInfoUtils#generateApplicationInfo( + * AndroidPackage, int, PackageUserState, int)} + */ + @Deprecated public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags, PackageUserState state, int userId) { if (ai == null) return null; @@ -7904,7 +7875,12 @@ public class PackageParser { return ai; } + /** + * @deprecated use {@link PackageInfoUtils#generatePermissionInfo( + * ComponentParseUtils.ParsedPermission, int)} + */ @UnsupportedAppUsage + @Deprecated public static final PermissionInfo generatePermissionInfo( Permission p, int flags) { if (p == null) return null; @@ -7916,7 +7892,12 @@ public class PackageParser { return pi; } + /** + * @deprecated use {@link PackageInfoUtils#generatePermissionGroupInfo( + * ComponentParseUtils.ParsedPermissionGroup, int)} + */ @UnsupportedAppUsage + @Deprecated public static final PermissionGroupInfo generatePermissionGroupInfo( PermissionGroup pg, int flags) { if (pg == null) return null; @@ -7928,6 +7909,10 @@ public class PackageParser { return pgi; } + /** + * @deprecated use {@link ComponentParseUtils.ParsedActivity} + */ + @Deprecated public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable { @UnsupportedAppUsage public final ActivityInfo info; @@ -8043,7 +8028,12 @@ public class PackageParser { }; } + /** + * @deprecated use {@link PackageInfoUtils#generateActivityInfo( + * AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)} + */ @UnsupportedAppUsage + @Deprecated public static final ActivityInfo generateActivityInfo(Activity a, int flags, PackageUserState state, int userId) { if (a == null) return null; @@ -8061,6 +8051,11 @@ public class PackageParser { return ai; } + /** + * @deprecated use {@link PackageInfoUtils#generateActivityInfo( + * AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)} + */ + @Deprecated public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags, PackageUserState state, int userId) { if (ai == null) return null; @@ -8074,6 +8069,10 @@ public class PackageParser { return ai; } + /** + * @deprecated use {@link ComponentParseUtils.ParsedService} + */ + @Deprecated public final static class Service extends Component<ServiceIntentInfo> implements Parcelable { @UnsupportedAppUsage public final ServiceInfo info; @@ -8135,7 +8134,12 @@ public class PackageParser { }; } + /** + * @deprecated use {@link PackageInfoUtils#generateServiceInfo( + * AndroidPackage, ComponentParseUtils.ParsedService, int, PackageUserState, int)} + */ @UnsupportedAppUsage + @Deprecated public static final ServiceInfo generateServiceInfo(Service s, int flags, PackageUserState state, int userId) { if (s == null) return null; @@ -8153,6 +8157,10 @@ public class PackageParser { return si; } + /** + * @deprecated use {@link ComponentParseUtils.ParsedProvider} + */ + @Deprecated public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable { @UnsupportedAppUsage public final ProviderInfo info; @@ -8233,7 +8241,12 @@ public class PackageParser { }; } + /** + * @deprecated use {@link PackageInfoUtils#generateProviderInfo( + * AndroidPackage, ComponentParseUtils.ParsedProvider, int, PackageUserState, int)} + */ @UnsupportedAppUsage + @Deprecated public static final ProviderInfo generateProviderInfo(Provider p, int flags, PackageUserState state, int userId) { if (p == null) return null; @@ -8256,6 +8269,10 @@ public class PackageParser { return pi; } + /** + * @deprecated use {@link ComponentParseUtils.ParsedInstrumentation} + */ + @Deprecated public final static class Instrumentation extends Component<IntentInfo> implements Parcelable { @UnsupportedAppUsage @@ -8316,7 +8333,12 @@ public class PackageParser { }; } + /** + * @deprecated use {@link PackageInfoUtils#generateInstrumentationInfo( + * ComponentParseUtils.ParsedInstrumentation, int)} + */ @UnsupportedAppUsage + @Deprecated public static final InstrumentationInfo generateInstrumentationInfo( Instrumentation i, int flags) { if (i == null) return null; @@ -8328,6 +8350,10 @@ public class PackageParser { return ii; } + /** + * @deprecated use {@link ComponentParseUtils.ParsedIntentInfo} + */ + @Deprecated public static abstract class IntentInfo extends IntentFilter { @UnsupportedAppUsage public boolean hasDefault; @@ -8371,8 +8397,10 @@ public class PackageParser { } } - public static final class QueriesIntentInfo extends IntentInfo {} - + /** + * @deprecated use {@link ComponentParseUtils.ParsedActivityIntentInfo} + */ + @Deprecated public final static class ActivityIntentInfo extends IntentInfo { @UnsupportedAppUsage public Activity activity; @@ -8396,6 +8424,10 @@ public class PackageParser { } } + /** + * @deprecated use {@link ComponentParseUtils.ParsedServiceIntentInfo} + */ + @Deprecated public final static class ServiceIntentInfo extends IntentInfo { @UnsupportedAppUsage public Service service; @@ -8419,6 +8451,10 @@ public class PackageParser { } } + /** + * @deprecated use {@link ComponentParseUtils.ParsedProviderIntentInfo} + */ + @Deprecated public static final class ProviderIntentInfo extends IntentInfo { @UnsupportedAppUsage public Provider provider; diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 5c74efb8ff1b..55574c3cb880 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -28,6 +28,7 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPON import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import android.annotation.UnsupportedAppUsage; +import android.content.pm.parsing.ComponentParseUtils; import android.os.BaseBundle; import android.os.Debug; import android.os.PersistableBundle; @@ -127,6 +128,18 @@ public class PackageUserState { && (!this.hidden || matchUninstalled)); } + public boolean isMatch(ComponentInfo componentInfo, int flags) { + return isMatch(componentInfo.applicationInfo.isSystemApp(), + componentInfo.applicationInfo.enabled, componentInfo.enabled, + componentInfo.directBootAware, componentInfo.name, flags); + } + + public boolean isMatch(boolean isSystem, boolean isPackageEnabled, + ComponentParseUtils.ParsedComponent component, int flags) { + return isMatch(isSystem, isPackageEnabled, component.isEnabled(), + component.isDirectBootAware(), component.getName(), flags); + } + /** * Test if the given component is considered installed, enabled and a match * for the given flags. @@ -135,28 +148,33 @@ public class PackageUserState { * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and * {@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}. * </p> + * */ - public boolean isMatch(ComponentInfo componentInfo, int flags) { - final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp(); + public boolean isMatch(boolean isSystem, boolean isPackageEnabled, boolean isComponentEnabled, + boolean isComponentDirectBootAware, String componentName, int flags) { final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0; - if (!isAvailable(flags) - && !(isSystemApp && matchUninstalled)) return reportIfDebug(false, flags); - if (!isEnabled(componentInfo, flags)) return reportIfDebug(false, flags); + if (!isAvailable(flags) && !(isSystem && matchUninstalled)) { + return reportIfDebug(false, flags); + } + + if (!isEnabled(isPackageEnabled, isComponentEnabled, componentName, flags)) { + return reportIfDebug(false, flags); + } if ((flags & MATCH_SYSTEM_ONLY) != 0) { - if (!isSystemApp) { + if (!isSystem) { return reportIfDebug(false, flags); } } final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) - && !componentInfo.directBootAware; + && !isComponentDirectBootAware; final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) - && componentInfo.directBootAware; + && isComponentDirectBootAware; return reportIfDebug(matchesUnaware || matchesAware, flags); } - private boolean reportIfDebug(boolean result, int flags) { + public boolean reportIfDebug(boolean result, int flags) { if (DEBUG && !result) { Slog.i(LOG_TAG, "No match!; flags: " + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " " @@ -165,10 +183,22 @@ public class PackageUserState { return result; } + public boolean isEnabled(ComponentInfo componentInfo, int flags) { + return isEnabled(componentInfo.applicationInfo.enabled, componentInfo.enabled, + componentInfo.name, flags); + } + + public boolean isEnabled(boolean isPackageEnabled, + ComponentParseUtils.ParsedComponent parsedComponent, int flags) { + return isEnabled(isPackageEnabled, parsedComponent.isEnabled(), parsedComponent.getName(), + flags); + } + /** * Test if the given component is considered enabled. */ - public boolean isEnabled(ComponentInfo componentInfo, int flags) { + public boolean isEnabled(boolean isPackageEnabled, boolean isComponentEnabled, + String componentName, int flags) { if ((flags & MATCH_DISABLED_COMPONENTS) != 0) { return true; } @@ -183,24 +213,26 @@ public class PackageUserState { if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) { return false; } + // fallthrough case COMPONENT_ENABLED_STATE_DEFAULT: - if (!componentInfo.applicationInfo.enabled) { + if (!isPackageEnabled) { return false; } + // fallthrough case COMPONENT_ENABLED_STATE_ENABLED: break; } // Check if component has explicit state before falling through to // the manifest default - if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) { + if (ArrayUtils.contains(this.enabledComponents, componentName)) { return true; } - if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) { + if (ArrayUtils.contains(this.disabledComponents, componentName)) { return false; } - return componentInfo.enabled; + return isComponentEnabled; } @Override diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 3488cc30892c..2863b268e795 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.parsing.AndroidPackage; import android.os.Parcel; import android.os.Parcelable; @@ -38,20 +39,24 @@ import java.util.List; public final class SharedLibraryInfo implements Parcelable { /** @hide */ - public static SharedLibraryInfo createForStatic(PackageParser.Package pkg) { - return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), - pkg.staticSharedLibName, - pkg.staticSharedLibVersion, + public static SharedLibraryInfo createForStatic(AndroidPackage pkg) { + return new SharedLibraryInfo(null, pkg.getPackageName(), + pkg.makeListAllCodePaths(), + pkg.getStaticSharedLibName(), + pkg.getStaticSharedLibVersion(), TYPE_STATIC, - new VersionedPackage(pkg.manifestPackageName, pkg.getLongVersionCode()), + new VersionedPackage(pkg.getManifestPackageName(), + pkg.getLongVersionCode()), null, null); } /** @hide */ - public static SharedLibraryInfo createForDynamic(PackageParser.Package pkg, String name) { - return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), name, + public static SharedLibraryInfo createForDynamic(AndroidPackage pkg, String name) { + return new SharedLibraryInfo(null, pkg.getPackageName(), + pkg.makeListAllCodePaths(), name, (long) VERSION_UNDEFINED, - TYPE_DYNAMIC, new VersionedPackage(pkg.packageName, pkg.getLongVersionCode()), + TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(), + pkg.getLongVersionCode()), null, null); } diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java index 5d10b8826b00..4cd201fbe538 100644 --- a/core/java/android/content/pm/dex/DexMetadataHelper.java +++ b/core/java/android/content/pm/dex/DexMetadataHelper.java @@ -22,6 +22,7 @@ import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.parsing.AndroidPackage; import android.util.ArrayMap; import android.util.jar.StrictJarFile; @@ -86,8 +87,8 @@ public class DexMetadataHelper { * * NOTE: involves I/O checks. */ - public static Map<String, String> getPackageDexMetadata(PackageParser.Package pkg) { - return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths()); + public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) { + return buildPackageApkToDexMetadataMap(pkg.makeListAllCodePaths()); } /** @@ -160,7 +161,7 @@ public class DexMetadataHelper { * * @throws PackageParserException in case of errors. */ - public static void validatePackageDexMetadata(PackageParser.Package pkg) + public static void validatePackageDexMetadata(AndroidPackage pkg) throws PackageParserException { Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values(); for (String dexMetadata : apkToDexMetadataList) { diff --git a/core/java/android/content/pm/parsing/AndroidPackage.aidl b/core/java/android/content/pm/parsing/AndroidPackage.aidl new file mode 100644 index 000000000000..ab3cf7cb8c65 --- /dev/null +++ b/core/java/android/content/pm/parsing/AndroidPackage.aidl @@ -0,0 +1,21 @@ +/* +** +** Copyright 2019, 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 android.content.pm.parsing; + +/* @hide */ +parcelable AndroidPackage; diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java new file mode 100644 index 000000000000..515185eaaf57 --- /dev/null +++ b/core/java/android/content/pm/parsing/AndroidPackage.java @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import android.annotation.Nullable; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageParser; +import android.content.pm.PackageUserState; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; +import android.content.pm.parsing.ComponentParseUtils.ParsedProvider; +import android.content.pm.parsing.ComponentParseUtils.ParsedService; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArraySet; +import android.util.SparseArray; + +import java.security.PublicKey; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * The last state of a package during parsing/install before it is available in + * {@link com.android.server.pm.PackageManagerService#mPackages}. + * + * It is the responsibility of the caller to understand what data is available at what step of the + * parsing or install process. + * + * TODO(b/135203078): Nullability annotations + * TODO(b/135203078): Remove get/setAppInfo differences + * + * @hide + */ +public interface AndroidPackage extends Parcelable { + + /** + * This will eventually be removed. Avoid calling this at all costs. + */ + @Deprecated + AndroidPackageWrite mutate(); + + boolean canHaveOatDir(); + + boolean cantSaveState(); + + List<String> getAdoptPermissions(); + + List<String> getAllCodePaths(); + + List<String> getAllCodePathsExcludingResourceOnly(); + + String getAppComponentFactory(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getClassLoaderName()} + */ + @Deprecated + String getAppInfoClassLoaderName(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getCodePath()} + */ + @Deprecated + String getAppInfoCodePath(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getName()} + */ + @Deprecated + String getAppInfoName(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getPackageName()} + */ + @Deprecated + String getAppInfoPackageName(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getProcessName()} + */ + @Deprecated + String getAppInfoProcessName(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getCodePath()} + */ + @Deprecated + String getAppInfoResourcePath(); + + Bundle getAppMetaData(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #getVolumeUuid()} + */ + @Deprecated + String getApplicationInfoVolumeUuid(); + + String getBackupAgentName(); + + int getBanner(); + + String getBaseCodePath(); + + int getBaseRevisionCode(); + + int getCategory(); + + String getClassLoaderName(); + + String getClassName(); + + String getCodePath(); + + int getCompatibleWidthLimitDp(); + + int getCompileSdkVersion(); + + String getCompileSdkVersionCodeName(); + + @Nullable + List<ConfigurationInfo> getConfigPreferences(); + + String getCpuAbiOverride(); + + String getCredentialProtectedDataDir(); + + String getDataDir(); + + int getDescriptionRes(); + + String getDeviceProtectedDataDir(); + + List<FeatureGroupInfo> getFeatureGroups(); + + int getFlags(); + + int getFullBackupContent(); + + int getHiddenApiEnforcementPolicy(); + + int getIcon(); + + int getIconRes(); + + List<String> getImplicitPermissions(); + + int getInstallLocation(); + + Map<String, ArraySet<PublicKey>> getKeySetMapping(); + + int getLabelRes(); + + int getLargestWidthLimitDp(); + + long[] getLastPackageUsageTimeInMills(); + + long getLatestForegroundPackageUseTimeInMills(); + + long getLatestPackageUseTimeInMills(); + + List<String> getLibraryNames(); + + int getLogo(); + + long getLongVersionCode(); + + String getManageSpaceActivityName(); + + String getManifestPackageName(); + + float getMaxAspectRatio(); + + Bundle getMetaData(); // TODO(b/135203078): Make all the Bundles immutable + + float getMinAspectRatio(); + + int getMinSdkVersion(); + + String getName(); + + String getNativeLibraryDir(); + + String getNativeLibraryRootDir(); + + int getNetworkSecurityConfigRes(); + + CharSequence getNonLocalizedLabel(); + + @Nullable + List<String> getOriginalPackages(); + + String getOverlayCategory(); + + int getOverlayPriority(); + + String getOverlayTarget(); + + String getOverlayTargetName(); + + // TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods? + // The refactor makes them the same value with no known consequences, so should be redundant. + String getPackageName(); + + @Nullable + List<ParsedActivity> getActivities(); + + @Nullable + List<ParsedInstrumentation> getInstrumentations(); + + @Nullable + List<ParsedPermissionGroup> getPermissionGroups(); + + @Nullable + List<ParsedPermission> getPermissions(); + + @Nullable + List<ParsedProvider> getProviders(); + + @Nullable + List<ParsedActivity> getReceivers(); + + @Nullable + List<ParsedService> getServices(); + + String getPermission(); + + @Nullable + List<ParsedActivityIntentInfo> getPreferredActivityFilters(); + + int getPreferredOrder(); + + String getPrimaryCpuAbi(); + + int getPrivateFlags(); + + String getProcessName(); + + @Nullable + List<String> getProtectedBroadcasts(); + + String getPublicSourceDir(); + + List<Intent> getQueriesIntents(); + + List<String> getQueriesPackages(); + + String getRealPackage(); + + // TODO(b/135203078): Rename to getRequiredFeatures? Somewhat ambiguous whether "Req" is + // required or requested. + @Nullable + List<FeatureInfo> getReqFeatures(); + + List<String> getRequestedPermissions(); + + String getRequiredAccountType(); + + int getRequiresSmallestWidthDp(); + + byte[] getRestrictUpdateHash(); + + String getRestrictedAccountType(); + + int getRoundIconRes(); + + String getScanPublicSourceDir(); + + String getScanSourceDir(); + + String getSeInfo(); + + String getSeInfoUser(); + + String getSecondaryCpuAbi(); + + String getSecondaryNativeLibraryDir(); + + String getSharedUserId(); + + int getSharedUserLabel(); + + PackageParser.SigningDetails getSigningDetails(); + + String[] getSplitClassLoaderNames(); + + @Nullable + String[] getSplitCodePaths(); + + @Nullable + SparseArray<int[]> getSplitDependencies(); + + int[] getSplitFlags(); + + String[] getSplitNames(); + + String[] getSplitPublicSourceDirs(); + + int[] getSplitRevisionCodes(); + + String getStaticSharedLibName(); + + long getStaticSharedLibVersion(); + + // TODO(b/135203078): Return String directly + UUID getStorageUuid(); + + int getTargetSandboxVersion(); + + int getTargetSdkVersion(); + + String getTaskAffinity(); + + int getTheme(); + + int getUiOptions(); + + int getUid(); + + Set<String> getUpgradeKeySets(); + + @Nullable + List<String> getUsesLibraries(); + + @Nullable + String[] getUsesLibraryFiles(); + + List<SharedLibraryInfo> getUsesLibraryInfos(); + + @Nullable + List<String> getUsesOptionalLibraries(); + + @Nullable + List<String> getUsesStaticLibraries(); + + @Nullable + String[][] getUsesStaticLibrariesCertDigests(); + + @Nullable + long[] getUsesStaticLibrariesVersions(); + + int getVersionCode(); + + int getVersionCodeMajor(); + + String getVersionName(); + + String getVolumeUuid(); + + String getZygotePreloadName(); + + boolean hasComponentClassName(String className); + + // App Info + + boolean hasRequestedLegacyExternalStorage(); + + boolean isBaseHardwareAccelerated(); + + boolean isCoreApp(); + + boolean isDefaultToDeviceProtectedStorage(); + + boolean isDirectBootAware(); + + boolean isEmbeddedDexUsed(); + + boolean isEnabled(); + + boolean isEncryptionAware(); + + boolean isExternal(); + + boolean isForceQueryable(); + + boolean isForwardLocked(); + + boolean isHiddenUntilInstalled(); + + boolean isInstantApp(); + + boolean isInternal(); + + boolean isLibrary(); + + // TODO(b/135203078): Should probably be in a utility class + boolean isMatch(int flags); + + boolean isNativeLibraryRootRequiresIsa(); + + boolean isOem(); + + boolean isOverlayIsStatic(); + + boolean isPrivileged(); + + boolean isProduct(); + + boolean isProfileableByShell(); + + boolean isRequiredForAllUsers(); + + boolean isStaticSharedLibrary(); + + boolean isStub(); + + boolean isSystem(); // TODO(b/135203078): Collapse with isSystemApp, should be exactly the same. + + boolean isSystemApp(); + + boolean isSystemExt(); + + boolean isUpdatedSystemApp(); + + boolean isUse32BitAbi(); + + boolean isVendor(); + + boolean isVisibleToInstantApps(); + + List<String> makeListAllCodePaths(); // TODO(b/135203078): Collapse with getAllCodePaths + + boolean requestsIsolatedSplitLoading(); + + /** + * Generates an {@link ApplicationInfo} object with only the data available in this object. + * + * This does not contain any system or user state data, and should be avoided. Prefer + * {@link PackageInfoUtils#generateApplicationInfo(AndroidPackage, int, PackageUserState, int)}. + */ + ApplicationInfo toAppInfoWithoutState(); + + Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() { + @Override + public PackageImpl createFromParcel(Parcel source) { + return new PackageImpl(source); + } + + @Override + public PackageImpl[] newArray(int size) { + return new PackageImpl[size]; + } + }; +} diff --git a/core/java/android/content/pm/parsing/AndroidPackageWrite.java b/core/java/android/content/pm/parsing/AndroidPackageWrite.java new file mode 100644 index 000000000000..b7595d2dd710 --- /dev/null +++ b/core/java/android/content/pm/parsing/AndroidPackageWrite.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import android.annotation.Nullable; +import android.content.pm.PackageParser; +import android.content.pm.SharedLibraryInfo; + +import java.util.List; + +/** + * Contains remaining mutable fields after package parsing has completed. + * + * Most are state that can probably be tracked outside of the AndroidPackage object. New methods + * should never be added to this interface. + * + * TODO(b/135203078): Remove entirely + * + * @deprecated the eventual goal is that the object returned from parsing represents exactly what + * was parsed from the APK, and so further mutation should be disallowed, + * with any state being stored in another class + * + * @hide + */ +@Deprecated +public interface AndroidPackageWrite extends AndroidPackage { + + AndroidPackageWrite setUsesLibraryFiles(@Nullable String[] usesLibraryFiles); + + // TODO(b/135203078): Remove or use a non-system wide representation of the shared libraries; + // this doesn't represent what was parsed from the APK + AndroidPackageWrite setUsesLibraryInfos(@Nullable List<SharedLibraryInfo> usesLibraryInfos); + + AndroidPackageWrite setHiddenUntilInstalled(boolean hidden); + + AndroidPackageWrite setUpdatedSystemApp(boolean updatedSystemApp); + + AndroidPackageWrite setLastPackageUsageTimeInMills(int reason, long time); + + AndroidPackageWrite setPrimaryCpuAbi(String primaryCpuAbi); + + AndroidPackageWrite setSeInfo(String seInfo); + + AndroidPackageWrite setSigningDetails(PackageParser.SigningDetails signingDetails); +} diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java new file mode 100644 index 000000000000..ac2e373f000d --- /dev/null +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + +import android.annotation.UnsupportedAppUsage; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.VerifierInfo; +import android.content.res.ApkAssets; +import android.content.res.XmlResourceParser; +import android.os.Trace; +import android.util.ArrayMap; +import android.util.AttributeSet; +import android.util.Pair; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.util.ArrayUtils; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** @hide */ +public class ApkLiteParseUtils { + + private static final String TAG = ApkParseUtils.TAG; + + // TODO(b/135203078): Consolidate constants + private static final int DEFAULT_MIN_SDK_VERSION = 1; + private static final int DEFAULT_TARGET_SDK_VERSION = 0; + + private static final int PARSE_DEFAULT_INSTALL_LOCATION = + PackageInfo.INSTALL_LOCATION_UNSPECIFIED; + + /** + * Parse only lightweight details about the package at the given location. + * Automatically detects if the package is a monolithic style (single APK + * file) or cluster style (directory of APKs). + * <p> + * This performs sanity checking on cluster style packages, such as + * requiring identical package name and version codes, a single base APK, + * and unique split names. + * + * @see PackageParser#parsePackage(File, int) + */ + @UnsupportedAppUsage + public static PackageParser.PackageLite parsePackageLite(File packageFile, int flags) + throws PackageParser.PackageParserException { + if (packageFile.isDirectory()) { + return parseClusterPackageLite(packageFile, flags); + } else { + return parseMonolithicPackageLite(packageFile, flags); + } + } + + public static PackageParser.PackageLite parseMonolithicPackageLite(File packageFile, int flags) + throws PackageParser.PackageParserException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); + final PackageParser.ApkLite baseApk = parseApkLite(packageFile, flags); + final String packagePath = packageFile.getAbsolutePath(); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + return new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null, + null, null); + } + + public static PackageParser.PackageLite parseClusterPackageLite(File packageDir, int flags) + throws PackageParser.PackageParserException { + final File[] files = packageDir.listFiles(); + if (ArrayUtils.isEmpty(files)) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split"); + } + + String packageName = null; + int versionCode = 0; + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); + final ArrayMap<String, PackageParser.ApkLite> apks = new ArrayMap<>(); + for (File file : files) { + if (PackageParser.isApkFile(file)) { + final PackageParser.ApkLite lite = parseApkLite(file, flags); + + // Assert that all package names and version codes are + // consistent with the first one we encounter. + if (packageName == null) { + packageName = lite.packageName; + versionCode = lite.versionCode; + } else { + if (!packageName.equals(lite.packageName)) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent package " + lite.packageName + " in " + file + + "; expected " + packageName); + } + if (versionCode != lite.versionCode) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent version " + lite.versionCode + " in " + file + + "; expected " + versionCode); + } + } + + // Assert that each split is defined only oncuses-static-libe + if (apks.put(lite.splitName, lite) != null) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Split name " + lite.splitName + + " defined more than once; most recent was " + file); + } + } + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + + final PackageParser.ApkLite baseApk = apks.remove(null); + if (baseApk == null) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Missing base APK in " + packageDir); + } + + // Always apply deterministic ordering based on splitName + final int size = apks.size(); + + String[] splitNames = null; + boolean[] isFeatureSplits = null; + String[] usesSplitNames = null; + String[] configForSplits = null; + String[] splitCodePaths = null; + int[] splitRevisionCodes = null; + if (size > 0) { + splitNames = new String[size]; + isFeatureSplits = new boolean[size]; + usesSplitNames = new String[size]; + configForSplits = new String[size]; + splitCodePaths = new String[size]; + splitRevisionCodes = new int[size]; + + splitNames = apks.keySet().toArray(splitNames); + Arrays.sort(splitNames, PackageParser.sSplitNameComparator); + + for (int i = 0; i < size; i++) { + final PackageParser.ApkLite apk = apks.get(splitNames[i]); + usesSplitNames[i] = apk.usesSplitName; + isFeatureSplits[i] = apk.isFeatureSplit; + configForSplits[i] = apk.configForSplit; + splitCodePaths[i] = apk.codePath; + splitRevisionCodes[i] = apk.revisionCode; + } + } + + final String codePath = packageDir.getAbsolutePath(); + return new PackageParser.PackageLite(codePath, baseApk, splitNames, isFeatureSplits, + usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes); + } + + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param apkFile path to a single APK + * @param flags optional parse flags, such as + * {@link PackageParser#PARSE_COLLECT_CERTIFICATES} + */ + public static PackageParser.ApkLite parseApkLite(File apkFile, int flags) + throws PackageParser.PackageParserException { + return parseApkLiteInner(apkFile, null, null, flags); + } + + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param fd already open file descriptor of an apk file + * @param debugPathName arbitrary text name for this file, for debug output + * @param flags optional parse flags, such as + * {@link PackageParser#PARSE_COLLECT_CERTIFICATES} + */ + public static PackageParser.ApkLite parseApkLite(FileDescriptor fd, String debugPathName, + int flags) throws PackageParser.PackageParserException { + return parseApkLiteInner(null, fd, debugPathName, flags); + } + + private static PackageParser.ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, + String debugPathName, int flags) throws PackageParser.PackageParserException { + final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); + + XmlResourceParser parser = null; + ApkAssets apkAssets = null; + try { + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_NOT_APK, + "Failed to parse " + apkPath, e); + } + + parser = apkAssets.openXml(PackageParser.ANDROID_MANIFEST_FILENAME); + + final PackageParser.SigningDetails signingDetails; + if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) { + final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); + try { + signingDetails = + ApkParseUtils.collectCertificates(apkFile.getAbsolutePath(), skipVerify, + false, PackageParser.SigningDetails.UNKNOWN); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } else { + signingDetails = PackageParser.SigningDetails.UNKNOWN; + } + + final AttributeSet attrs = parser; + return parseApkLite(apkPath, parser, attrs, signingDetails); + + } catch (XmlPullParserException | IOException | RuntimeException e) { + Slog.w(TAG, "Failed to parse " + apkPath, e); + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to parse " + apkPath, e); + } finally { + IoUtils.closeQuietly(parser); + if (apkAssets != null) { + try { + apkAssets.close(); + } catch (Throwable ignored) { + } + } + // TODO(b/72056911): Implement AutoCloseable on ApkAssets. + } + } + + private static PackageParser.ApkLite parseApkLite( + String codePath, XmlPullParser parser, AttributeSet attrs, + PackageParser.SigningDetails signingDetails) + throws IOException, XmlPullParserException, PackageParser.PackageParserException { + final Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames( + parser, attrs); + + int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; + int versionCode = 0; + int versionCodeMajor = 0; + int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION; + int minSdkVersion = DEFAULT_MIN_SDK_VERSION; + int revisionCode = 0; + boolean coreApp = false; + boolean debuggable = false; + boolean multiArch = false; + boolean use32bitAbi = false; + boolean extractNativeLibs = true; + boolean isolatedSplits = false; + boolean isFeatureSplit = false; + boolean isSplitRequired = false; + boolean useEmbeddedDex = false; + String configForSplit = null; + String usesSplitName = null; + + for (int i = 0; i < attrs.getAttributeCount(); i++) { + final String attr = attrs.getAttributeName(i); + switch (attr) { + case "installLocation": + installLocation = attrs.getAttributeIntValue(i, + PARSE_DEFAULT_INSTALL_LOCATION); + break; + case "versionCode": + versionCode = attrs.getAttributeIntValue(i, 0); + break; + case "versionCodeMajor": + versionCodeMajor = attrs.getAttributeIntValue(i, 0); + break; + case "revisionCode": + revisionCode = attrs.getAttributeIntValue(i, 0); + break; + case "coreApp": + coreApp = attrs.getAttributeBooleanValue(i, false); + break; + case "isolatedSplits": + isolatedSplits = attrs.getAttributeBooleanValue(i, false); + break; + case "configForSplit": + configForSplit = attrs.getAttributeValue(i); + break; + case "isFeatureSplit": + isFeatureSplit = attrs.getAttributeBooleanValue(i, false); + break; + case "isSplitRequired": + isSplitRequired = attrs.getAttributeBooleanValue(i, false); + break; + } + } + + // Only search the tree when the tag is the direct child of <manifest> tag + int type; + final int searchDepth = parser.getDepth() + 1; + + final List<VerifierInfo> verifiers = new ArrayList<>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getDepth() != searchDepth) { + continue; + } + + if (PackageParser.TAG_PACKAGE_VERIFIER.equals(parser.getName())) { + final VerifierInfo verifier = parseVerifier(attrs); + if (verifier != null) { + verifiers.add(verifier); + } + } else if (PackageParser.TAG_APPLICATION.equals(parser.getName())) { + for (int i = 0; i < attrs.getAttributeCount(); ++i) { + final String attr = attrs.getAttributeName(i); + switch (attr) { + case "debuggable": + debuggable = attrs.getAttributeBooleanValue(i, false); + break; + case "multiArch": + multiArch = attrs.getAttributeBooleanValue(i, false); + break; + case "use32bitAbi": + use32bitAbi = attrs.getAttributeBooleanValue(i, false); + break; + case "extractNativeLibs": + extractNativeLibs = attrs.getAttributeBooleanValue(i, true); + break; + case "useEmbeddedDex": + useEmbeddedDex = attrs.getAttributeBooleanValue(i, false); + break; + } + } + } else if (PackageParser.TAG_USES_SPLIT.equals(parser.getName())) { + if (usesSplitName != null) { + Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others."); + continue; + } + + usesSplitName = attrs.getAttributeValue(PackageParser.ANDROID_RESOURCES, "name"); + if (usesSplitName == null) { + throw new PackageParser.PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "<uses-split> tag requires 'android:name' attribute"); + } + } else if (PackageParser.TAG_USES_SDK.equals(parser.getName())) { + for (int i = 0; i < attrs.getAttributeCount(); ++i) { + final String attr = attrs.getAttributeName(i); + if ("targetSdkVersion".equals(attr)) { + targetSdkVersion = attrs.getAttributeIntValue(i, + DEFAULT_TARGET_SDK_VERSION); + } + if ("minSdkVersion".equals(attr)) { + minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION); + } + } + } + } + + return new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second, + isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode, + versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails, + coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, + isolatedSplits, minSdkVersion, targetSdkVersion); + } + + public static VerifierInfo parseVerifier(AttributeSet attrs) { + String packageName = null; + String encodedPublicKey = null; + + final int attrCount = attrs.getAttributeCount(); + for (int i = 0; i < attrCount; i++) { + final int attrResId = attrs.getAttributeNameResource(i); + switch (attrResId) { + case R.attr.name: + packageName = attrs.getAttributeValue(i); + break; + + case R.attr.publicKey: + encodedPublicKey = attrs.getAttributeValue(i); + break; + } + } + + if (packageName == null || packageName.length() == 0) { + Slog.i(TAG, "verifier package name was null; skipping"); + return null; + } + + final PublicKey publicKey = PackageParser.parsePublicKey(encodedPublicKey); + if (publicKey == null) { + Slog.i(TAG, "Unable to parse verifier public key for " + packageName); + return null; + } + + return new VerifierInfo(packageName, publicKey); + } +} diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java new file mode 100644 index 000000000000..edbf73a0c0da --- /dev/null +++ b/core/java/android/content/pm/parsing/ApkParseUtils.java @@ -0,0 +1,3187 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.PackageManager.FEATURE_WATCH; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; +import static android.os.Build.VERSION_CODES.O; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityTaskManager; +import android.app.ActivityThread; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.SigningDetails; +import android.content.pm.Signature; +import android.content.pm.permission.SplitPermissionInfoParcelable; +import android.content.pm.split.DefaultSplitAssetLoader; +import android.content.pm.split.SplitAssetDependencyLoader; +import android.content.pm.split.SplitAssetLoader; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.FileUtils; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.os.Trace; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TypedValue; +import android.util.apk.ApkSignatureVerifier; + +import com.android.internal.R; +import com.android.internal.os.ClassLoaderFactory; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.XmlUtils; + +import libcore.io.IoUtils; +import libcore.util.EmptyArray; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.IOException; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** @hide */ +public class ApkParseUtils { + + // TODO(b/135203078): Consolidate log tags + static final String TAG = "PackageParsing"; + + /** + * Parse the package at the given location. Automatically detects if the + * package is a monolithic style (single APK file) or cluster style + * (directory of APKs). + * <p> + * This performs sanity checking on cluster style packages, such as + * requiring identical package name and version codes, a single base APK, + * and unique split names. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(ParsedPackage, boolean)}. + * + * If {@code useCaches} is true, the package parser might return a cached + * result from a previous parse of the same {@code packageFile} with the same + * {@code flags}. Note that this method does not check whether {@code packageFile} + * has changed since the last parse, it's up to callers to do so. + * + * @see PackageParser#parsePackageLite(File, int) + */ + public static ParsingPackage parsePackage( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + DisplayMetrics displayMetrics, + boolean onlyCoreApps, + File packageFile, + int flags + ) throws PackageParserException { + if (packageFile.isDirectory()) { + return parseClusterPackage(parseInput, separateProcesses, callback, displayMetrics, + onlyCoreApps, packageFile, flags); + } else { + return parseMonolithicPackage(parseInput, separateProcesses, callback, displayMetrics, + onlyCoreApps, packageFile, flags); + } + } + + /** + * Parse all APKs contained in the given directory, treating them as a + * single package. This also performs sanity checking, such as requiring + * identical package name and version codes, a single base APK, and unique + * split names. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(ParsedPackage, boolean)}. + */ + private static ParsingPackage parseClusterPackage( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + DisplayMetrics displayMetrics, + boolean onlyCoreApps, + File packageDir, + int flags + ) throws PackageParserException { + final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir, + 0); + if (onlyCoreApps && !lite.coreApp) { + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Not a coreApp: " + packageDir); + } + + // Build the split dependency tree. + SparseArray<int[]> splitDependencies = null; + final SplitAssetLoader assetLoader; + if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) { + try { + splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite); + assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags); + } catch (SplitAssetDependencyLoader.IllegalDependencyException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage()); + } + } else { + assetLoader = new DefaultSplitAssetLoader(lite, flags); + } + + try { + final AssetManager assets = assetLoader.getBaseAssetManager(); + final File baseApk = new File(lite.baseCodePath); + ParsingPackage parsingPackage = parseBaseApk(parseInput, separateProcesses, callback, + displayMetrics, baseApk, assets, flags); + if (parsingPackage == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Failed to parse base APK: " + baseApk); + } + + if (!ArrayUtils.isEmpty(lite.splitNames)) { + parsingPackage.asSplit( + lite.splitNames, + lite.splitCodePaths, + lite.splitRevisionCodes, + splitDependencies + ); + final int num = lite.splitNames.length; + + for (int i = 0; i < num; i++) { + final AssetManager splitAssets = assetLoader.getSplitAssetManager(i); + parseSplitApk(parseInput, displayMetrics, separateProcesses, parsingPackage, i, + splitAssets, flags); + } + } + + return parsingPackage.setCodePath(packageDir.getCanonicalPath()) + .setUse32BitAbi(lite.use32bitAbi); + } catch (IOException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to get path: " + lite.baseCodePath, e); + } finally { + IoUtils.closeQuietly(assetLoader); + } + } + + /** + * Parse the given APK file, treating it as as a single monolithic package. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(AndroidPackage, boolean)}. + */ + public static ParsingPackage parseMonolithicPackage( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + DisplayMetrics displayMetrics, + boolean onlyCoreApps, + File apkFile, + int flags + ) throws PackageParserException { + final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile, + flags); + if (onlyCoreApps) { + if (!lite.coreApp) { + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Not a coreApp: " + apkFile); + } + } + + final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); + try { + return parseBaseApk(parseInput, separateProcesses, callback, + displayMetrics, apkFile, assetLoader.getBaseAssetManager(), flags) + .setCodePath(apkFile.getCanonicalPath()) + .setUse32BitAbi(lite.use32bitAbi); + } catch (IOException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to get path: " + apkFile, e); + } finally { + IoUtils.closeQuietly(assetLoader); + } + } + + private static ParsingPackage parseBaseApk( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + DisplayMetrics displayMetrics, + File apkFile, + AssetManager assets, + int flags + ) throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + + String volumeUuid = null; + if (apkPath.startsWith(PackageParser.MNT_EXPAND)) { + final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length()); + volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end); + } + + if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); + + XmlResourceParser parser = null; + try { + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + parser = assets.openXmlResourceParser(cookie, PackageParser.ANDROID_MANIFEST_FILENAME); + final Resources res = new Resources(assets, displayMetrics, null); + + ParseResult result = parseBaseApk(parseInput, separateProcesses, callback, apkPath, res, + parser, flags); + if (!result.isSuccess()) { + throw new PackageParserException(result.getParseError(), + apkPath + " (at " + parser.getPositionDescription() + "): " + + result.getErrorMessage()); + } + + return result.getResultAndNull() + .setVolumeUuid(volumeUuid) + .setApplicationVolumeUuid(volumeUuid) + .setSigningDetails(SigningDetails.UNKNOWN); + } catch (PackageParserException e) { + throw e; + } catch (Exception e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to read manifest from " + apkPath, e); + } finally { + IoUtils.closeQuietly(parser); + } + } + + private static void parseSplitApk( + ParseInput parseInput, + DisplayMetrics displayMetrics, + String[] separateProcesses, + ParsingPackage parsingPackage, + int splitIndex, + AssetManager assets, + int flags + ) throws PackageParserException { + final String apkPath = parsingPackage.getSplitCodePaths()[splitIndex]; + + if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); + + final Resources res; + XmlResourceParser parser = null; + try { + // This must always succeed, as the path has been added to the AssetManager before. + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + + parser = assets.openXmlResourceParser(cookie, PackageParser.ANDROID_MANIFEST_FILENAME); + res = new Resources(assets, displayMetrics, null); + + final String[] outError = new String[1]; + ParseResult parseResult = parseSplitApk(parseInput, separateProcesses, parsingPackage, + res, parser, flags, splitIndex, outError); + if (!parseResult.isSuccess()) { + throw new PackageParserException(parseResult.getParseError(), + apkPath + " (at " + parser.getPositionDescription() + "): " + + parseResult.getErrorMessage()); + } + } catch (PackageParserException e) { + throw e; + } catch (Exception e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to read manifest from " + apkPath, e); + } finally { + IoUtils.closeQuietly(parser); + } + } + + /** + * Parse the manifest of a <em>base APK</em>. When adding new features you + * need to consider whether they should be supported by split APKs and child + * packages. + * + * @param apkPath The package apk file path + * @param res The resources from which to resolve values + * @param parser The manifest parser + * @param flags Flags how to parse + * @return Parsed package or null on error. + */ + private static ParseResult parseBaseApk( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + String apkPath, + Resources res, + XmlResourceParser parser, + int flags + ) throws XmlPullParserException, IOException { + final String splitName; + final String pkgName; + + try { + Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser, + parser); + pkgName = packageSplit.first; + splitName = packageSplit.second; + + if (!TextUtils.isEmpty(splitName)) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Expected base APK, but found split " + splitName + ); + } + } catch (PackageParserException e) { + return parseInput.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME); + } + + TypedArray manifestArray = null; + + try { + manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); + + boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false); + + ParsingPackage parsingPackage = PackageImpl.forParsing( + pkgName, + apkPath, + manifestArray, + isCoreApp + ); + + ParseResult result = parseBaseApkTags(parseInput, separateProcesses, callback, + parsingPackage, manifestArray, res, parser, flags); + if (!result.isSuccess()) { + return result; + } + + return parseInput.success(parsingPackage); + } finally { + if (manifestArray != null) { + manifestArray.recycle(); + } + } + } + + /** + * Parse the manifest of a <em>split APK</em>. + * <p> + * Note that split APKs have many more restrictions on what they're capable + * of doing, so many valid features of a base APK have been carefully + * omitted here. + * + * @param parsingPackage builder to fill + * @return false on failure + */ + private static ParseResult parseSplitApk( + ParseInput parseInput, + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + int flags, + int splitIndex, + String[] outError + ) throws XmlPullParserException, IOException, PackageParserException { + AttributeSet attrs = parser; + + // We parsed manifest tag earlier; just skip past it + PackageParser.parsePackageSplitNames(parser, attrs); + + int type; + + boolean foundApp = false; + + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals(PackageParser.TAG_APPLICATION)) { + if (foundApp) { + if (PackageParser.RIGID_PARSER) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "<manifest> has more than one <application>" + ); + } else { + Slog.w(TAG, "<manifest> has more than one <application>"); + XmlUtils.skipCurrentTag(parser); + continue; + } + } + + foundApp = true; + ParseResult parseResult = parseSplitApplication(parseInput, separateProcesses, + parsingPackage, res, + parser, flags, + splitIndex, outError); + if (!parseResult.isSuccess()) { + return parseResult; + } + + } else if (PackageParser.RIGID_PARSER) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad element under <manifest>: " + parser.getName() + ); + + } else { + Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName() + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + } + + if (!foundApp) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY, + "<manifest> does not contain an <application>" + ); + } + + return parseInput.success(parsingPackage); + } + + /** + * Parse the {@code application} XML tree at the current parse location in a + * <em>split APK</em> manifest. + * <p> + * Note that split APKs have many more restrictions on what they're capable + * of doing, so many valid features of a base APK have been carefully + * omitted here. + */ + private static ParseResult parseSplitApplication( + ParseInput parseInput, + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + int flags, + int splitIndex, + String[] outError + ) throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); + + parsingPackage.setSplitHasCode(splitIndex, sa.getBoolean( + R.styleable.AndroidManifestApplication_hasCode, true)); + + final String classLoaderName = sa.getString( + R.styleable.AndroidManifestApplication_classLoader); + if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) { + parsingPackage.setSplitClassLoaderName(splitIndex, classLoaderName); + } else { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Invalid class loader name: " + classLoaderName + ); + } + + final int innerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + ComponentParseUtils.ParsedComponent parsedComponent = null; + + String tagName = parser.getName(); + switch (tagName) { + case "activity": + ComponentParseUtils.ParsedActivity activity = + ComponentParseUtils.parseActivity(separateProcesses, + parsingPackage, + res, parser, flags, + outError, + false, + parsingPackage.isBaseHardwareAccelerated()); + if (activity == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addActivity(activity); + parsedComponent = activity; + break; + case "receiver": + activity = ComponentParseUtils.parseActivity( + separateProcesses, parsingPackage, + res, parser, flags, outError, + true, false); + if (activity == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addReceiver(activity); + parsedComponent = activity; + break; + case "service": + ComponentParseUtils.ParsedService s = ComponentParseUtils.parseService( + separateProcesses, + parsingPackage, + res, parser, flags, outError + ); + if (s == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addService(s); + parsedComponent = s; + break; + case "provider": + ComponentParseUtils.ParsedProvider p = ComponentParseUtils.parseProvider( + separateProcesses, + parsingPackage, + res, parser, flags, outError); + if (p == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addProvider(p); + parsedComponent = p; + break; + case "activity-alias": + activity = ComponentParseUtils.parseActivityAlias( + parsingPackage, + res, + parser, + outError + ); + if (activity == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addActivity(activity); + parsedComponent = activity; + break; + case "meta-data": + // note: application meta-data is stored off to the side, so it can + // remain null in the primary copy (we like to avoid extra copies because + // it can be large) + Bundle appMetaData = parseMetaData(parsingPackage, res, parser, + parsingPackage.getAppMetaData(), + outError); + if (appMetaData == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.setAppMetaData(appMetaData); + break; + case "uses-static-library": + ParseResult parseResult = parseUsesStaticLibrary(parseInput, parsingPackage, + res, parser); + if (!parseResult.isSuccess()) { + return parseResult; + } + + break; + case "uses-library": + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary); + + // Note: don't allow this value to be a reference to a resource + // that may change. + String lname = sa.getNonResourceString( + R.styleable.AndroidManifestUsesLibrary_name); + boolean req = sa.getBoolean( + R.styleable.AndroidManifestUsesLibrary_required, true); + + sa.recycle(); + + if (lname != null) { + lname = lname.intern(); + if (req) { + // Upgrade to treat as stronger constraint + parsingPackage.addUsesLibrary(lname) + .removeUsesOptionalLibrary(lname); + } else { + // Ignore if someone already defined as required + if (!ArrayUtils.contains(parsingPackage.getUsesLibraries(), lname)) { + parsingPackage.addUsesOptionalLibrary(lname); + } + } + } + + XmlUtils.skipCurrentTag(parser); + break; + case "uses-package": + // Dependencies for app installers; we don't currently try to + // enforce this. + XmlUtils.skipCurrentTag(parser); + break; + default: + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <application>: " + tagName + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad element under <application>: " + tagName + ); + } + } + + if (parsedComponent != null && parsedComponent.getSplitName() == null) { + // If the loaded component did not specify a split, inherit the split name + // based on the split it is defined in. + // This is used to later load the correct split when starting this + // component. + parsedComponent.setSplitName(parsingPackage.getSplitNames()[splitIndex]); + } + } + + return parseInput.success(parsingPackage); + } + + private static ParseResult parseBaseApkTags( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + ParsingPackage parsingPackage, + TypedArray manifestArray, + Resources res, + XmlResourceParser parser, + int flags + ) throws XmlPullParserException, IOException { + int type; + boolean foundApp = false; + + TypedArray sa = manifestArray; + + ParseResult sharedUserResult = parseSharedUser(parseInput, parsingPackage, sa); + if (!sharedUserResult.isSuccess()) { + return sharedUserResult; + } + + parseManifestAttributes(sa, parsingPackage, flags); + + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + + // All methods return a boolean, even if they can't fail. This can be enforced + // by making this final and not assigned, forcing the switch to assign success + // once in every branch. + final boolean success; + ParseResult parseResult = null; + + // TODO(b/135203078): Either use all booleans or all ParseResults + // TODO(b/135203078): Convert to instance methods to share variables + switch (tagName) { + case PackageParser.TAG_APPLICATION: + if (foundApp) { + if (PackageParser.RIGID_PARSER) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "<manifest> has more than one <application>" + ); + } else { + Slog.w(TAG, "<manifest> has more than one <application>"); + XmlUtils.skipCurrentTag(parser); + success = true; + } + } else { + foundApp = true; + parseResult = parseBaseApplication(parseInput, separateProcesses, + callback, + parsingPackage, res, parser, flags); + success = parseResult.isSuccess(); + } + break; + case PackageParser.TAG_OVERLAY: + parseResult = parseOverlay(parseInput, parsingPackage, res, parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_KEY_SETS: + parseResult = parseKeySets(parseInput, parsingPackage, res, parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_PERMISSION_GROUP: + parseResult = parsePermissionGroup(parseInput, parsingPackage, res, + parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_PERMISSION: + parseResult = parsePermission(parseInput, parsingPackage, res, parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_PERMISSION_TREE: + parseResult = parsePermissionTree(parseInput, parsingPackage, res, parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_USES_PERMISSION: + case PackageParser.TAG_USES_PERMISSION_SDK_M: + case PackageParser.TAG_USES_PERMISSION_SDK_23: + parseResult = parseUsesPermission(parseInput, parsingPackage, res, parser, + callback); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_USES_CONFIGURATION: + success = parseUsesConfiguration(parsingPackage, res, parser); + break; + case PackageParser.TAG_USES_FEATURE: + success = parseUsesFeature(parsingPackage, res, parser); + break; + case PackageParser.TAG_FEATURE_GROUP: + success = parseFeatureGroup(parsingPackage, res, parser); + break; + case PackageParser.TAG_USES_SDK: + parseResult = parseUsesSdk(parseInput, parsingPackage, res, parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_SUPPORT_SCREENS: + success = parseSupportScreens(parsingPackage, res, parser); + break; + case PackageParser.TAG_PROTECTED_BROADCAST: + success = parseProtectedBroadcast(parsingPackage, res, parser); + break; + case PackageParser.TAG_INSTRUMENTATION: + parseResult = parseInstrumentation(parseInput, parsingPackage, res, + parser); + success = parseResult.isSuccess(); + break; + case PackageParser.TAG_ORIGINAL_PACKAGE: + success = parseOriginalPackage(parsingPackage, res, parser); + break; + case PackageParser.TAG_ADOPT_PERMISSIONS: + success = parseAdoptPermissions(parsingPackage, res, parser); + break; + case PackageParser.TAG_USES_GL_TEXTURE: + case PackageParser.TAG_COMPATIBLE_SCREENS: + case PackageParser.TAG_SUPPORTS_INPUT: + case PackageParser.TAG_EAT_COMMENT: + // Just skip this tag + XmlUtils.skipCurrentTag(parser); + success = true; + break; + case PackageParser.TAG_RESTRICT_UPDATE: + success = parseRestrictUpdateHash(flags, parsingPackage, res, parser); + break; + case PackageParser.TAG_QUERIES: + parseResult = parseQueries(parseInput, parsingPackage, res, parser); + success = parseResult.isSuccess(); + break; + default: + parseResult = parseUnknownTag(parseInput, parsingPackage, parser); + success = parseResult.isSuccess(); + break; + } + + if (parseResult != null && !parseResult.isSuccess()) { + return parseResult; + } + + if (!success) { + return parseResult; + } + } + + if (!foundApp && ArrayUtils.size(parsingPackage.getInstrumentations()) == 0) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY, + "<manifest> does not contain an <application> or <instrumentation>" + ); + } + + convertNewPermissions(parsingPackage); + + convertSplitPermissions(parsingPackage); + + // At this point we can check if an application is not supporting densities and hence + // cannot be windowed / resized. Note that an SDK version of 0 is common for + // pre-Doughnut applications. + if (parsingPackage.usesCompatibilityMode()) { + adjustPackageToBeUnresizeableAndUnpipable(parsingPackage); + } + + return parseInput.success(parsingPackage); + } + + private static ParseResult parseUnknownTag( + ParseInput parseInput, + ParsingPackage parsingPackage, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + if (PackageParser.RIGID_PARSER) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad element under <manifest>: " + parser.getName() + ); + } else { + Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName() + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + return parseInput.success(parsingPackage); + } + } + + private static ParseResult parseSharedUser( + ParseInput parseInput, + ParsingPackage parsingPackage, + TypedArray manifestArray + ) { + String str = manifestArray.getNonConfigurationString( + R.styleable.AndroidManifest_sharedUserId, 0); + if (TextUtils.isEmpty(str)) { + return parseInput.success(parsingPackage); + } + + String nameError = validateName(str, true, true); + if (nameError != null && !"android".equals(parsingPackage.getPackageName())) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, + "<manifest> specifies bad sharedUserId name \"" + str + "\": " + + nameError + ); + } + + int sharedUserLabel = manifestArray.getResourceId( + R.styleable.AndroidManifest_sharedUserLabel, 0); + parsingPackage.setSharedUserId(str.intern()) + .setSharedUserLabel(sharedUserLabel); + + return parseInput.success(parsingPackage); + } + + private static void parseManifestAttributes( + TypedArray manifestArray, + ParsingPackage parsingPackage, + int flags + ) { + int installLocation = manifestArray.getInteger(R.styleable.AndroidManifest_installLocation, + PackageParser.PARSE_DEFAULT_INSTALL_LOCATION); + + final int targetSandboxVersion = manifestArray.getInteger( + R.styleable.AndroidManifest_targetSandboxVersion, + PackageParser.PARSE_DEFAULT_TARGET_SANDBOX); + + parsingPackage.setInstallLocation(installLocation) + .setTargetSandboxVersion(targetSandboxVersion); + + /* Set the global "on SD card" flag */ + parsingPackage.setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0); + + parsingPackage.setIsolatedSplitLoading(manifestArray.getBoolean( + R.styleable.AndroidManifest_isolatedSplits, false)); + } + + private static ParseResult parseKeySets( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws XmlPullParserException, IOException { + // we've encountered the 'key-sets' tag + // all the keys and keysets that we want must be defined here + // so we're going to iterate over the parser and pull out the things we want + int outerDepth = parser.getDepth(); + int currentKeySetDepth = -1; + int type; + String currentKeySet = null; + ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>(); + ArraySet<String> upgradeKeySets = new ArraySet<>(); + ArrayMap<String, ArraySet<String>> definedKeySets = + new ArrayMap<>(); + ArraySet<String> improperKeySets = new ArraySet<>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG) { + if (parser.getDepth() == currentKeySetDepth) { + currentKeySet = null; + currentKeySetDepth = -1; + } + continue; + } + String tagName = parser.getName(); + if (tagName.equals("key-set")) { + if (currentKeySet != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Improperly nested 'key-set' tag at " + parser.getPositionDescription() + ); + } + final TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestKeySet); + final String keysetName = sa.getNonResourceString( + R.styleable.AndroidManifestKeySet_name); + definedKeySets.put(keysetName, new ArraySet<>()); + currentKeySet = keysetName; + currentKeySetDepth = parser.getDepth(); + sa.recycle(); + } else if (tagName.equals("public-key")) { + if (currentKeySet == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Improperly nested 'key-set' tag at " + parser.getPositionDescription() + ); + } + final TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestPublicKey); + final String publicKeyName = sa.getNonResourceString( + R.styleable.AndroidManifestPublicKey_name); + final String encodedKey = sa.getNonResourceString( + R.styleable.AndroidManifestPublicKey_value); + if (encodedKey == null && publicKeys.get(publicKeyName) == null) { + sa.recycle(); + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "'public-key' " + publicKeyName + " must define a public-key value" + + " on first use at " + parser.getPositionDescription() + ); + } else if (encodedKey != null) { + PublicKey currentKey = PackageParser.parsePublicKey(encodedKey); + if (currentKey == null) { + Slog.w(TAG, "No recognized valid key in 'public-key' tag at " + + parser.getPositionDescription() + " key-set " + currentKeySet + + " will not be added to the package's defined key-sets."); + sa.recycle(); + improperKeySets.add(currentKeySet); + XmlUtils.skipCurrentTag(parser); + continue; + } + if (publicKeys.get(publicKeyName) == null + || publicKeys.get(publicKeyName).equals(currentKey)) { + + /* public-key first definition, or matches old definition */ + publicKeys.put(publicKeyName, currentKey); + } else { + sa.recycle(); + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Value of 'public-key' " + publicKeyName + + " conflicts with previously defined value at " + + parser.getPositionDescription() + ); + } + } + definedKeySets.get(currentKeySet).add(publicKeyName); + sa.recycle(); + XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("upgrade-key-set")) { + final TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUpgradeKeySet); + String name = sa.getNonResourceString( + R.styleable.AndroidManifestUpgradeKeySet_name); + upgradeKeySets.add(name); + sa.recycle(); + XmlUtils.skipCurrentTag(parser); + } else if (PackageParser.RIGID_PARSER) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad element under <key-sets>: " + parser.getName() + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription() + ); + } else { + Slog.w(TAG, "Unknown element under <key-sets>: " + parser.getName() + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + } + String packageName = parsingPackage.getPackageName(); + Set<String> publicKeyNames = publicKeys.keySet(); + if (publicKeyNames.removeAll(definedKeySets.keySet())) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Package" + packageName + " AndroidManifest.xml " + + "'key-set' and 'public-key' names must be distinct." + ); + } + + for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) { + final String keySetName = e.getKey(); + if (e.getValue().size() == 0) { + Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml " + + "'key-set' " + keySetName + " has no valid associated 'public-key'." + + " Not including in package's defined key-sets."); + continue; + } else if (improperKeySets.contains(keySetName)) { + Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml " + + "'key-set' " + keySetName + " contained improper 'public-key'" + + " tags. Not including in package's defined key-sets."); + continue; + } + + for (String s : e.getValue()) { + parsingPackage.addKeySet(keySetName, publicKeys.get(s)); + } + } + if (parsingPackage.getKeySetMapping().keySet().containsAll(upgradeKeySets)) { + parsingPackage.setUpgradeKeySets(upgradeKeySets); + } else { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Package" + packageName + " AndroidManifest.xml " + + "does not define all 'upgrade-key-set's ." + ); + } + + return parseInput.success(parsingPackage); + } + + public static boolean parsePackageItemInfo(String packageName, PackageItemInfo outInfo, + String[] outError, String tag, TypedArray sa, boolean nameRequired, + int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) { + // This case can only happen in unit tests where we sometimes need to create fakes + // of various package parser data structures. + if (sa == null) { + outError[0] = tag + " does not contain any attributes"; + return false; + } + + String name = sa.getNonConfigurationString(nameRes, 0); + if (name == null) { + if (nameRequired) { + outError[0] = tag + " does not specify android:name"; + return false; + } + } else { + String outInfoName = buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) { + outError[0] = tag + " invalid android:name"; + return false; + } + outInfo.name = outInfoName; + if (outInfoName == null) { + return false; + } + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0; + if (roundIconVal != 0) { + outInfo.icon = roundIconVal; + outInfo.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(iconRes, 0); + if (iconVal != 0) { + outInfo.icon = iconVal; + outInfo.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(logoRes, 0); + if (logoVal != 0) { + outInfo.logo = logoVal; + } + + int bannerVal = sa.getResourceId(bannerRes, 0); + if (bannerVal != 0) { + outInfo.banner = bannerVal; + } + + TypedValue v = sa.peekValue(labelRes); + if (v != null && (outInfo.labelRes = v.resourceId) == 0) { + outInfo.nonLocalizedLabel = v.coerceToString(); + } + + outInfo.packageName = packageName; + + return true; + } + + private static ParseResult parsePackageItemInfo( + ParseInput parseInput, + ParsingPackage parsingPackage, + String tag, + TypedArray sa, + boolean nameRequired, + int nameRes, + int labelRes, + int iconRes, + int roundIconRes, + int logoRes, + int bannerRes + ) { + // This case can only happen in unit tests where we sometimes need to create fakes + // of various package parser data structures. + if (sa == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + tag + " does not contain any attributes" + ); + } + + String name = sa.getNonConfigurationString(nameRes, 0); + if (name == null) { + if (nameRequired) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + tag + " does not specify android:name" + ); + } + } else { + String packageName = parsingPackage.getPackageName(); + String outInfoName = buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + tag + " invalid android:name" + ); + } else if (outInfoName == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Empty class name in package " + packageName + ); + } + + parsingPackage.setName(outInfoName); + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0; + if (roundIconVal != 0) { + parsingPackage.setIcon(roundIconVal) + .setNonLocalizedLabel(null); + } else { + int iconVal = sa.getResourceId(iconRes, 0); + if (iconVal != 0) { + parsingPackage.setIcon(iconVal) + .setNonLocalizedLabel(null); + } + } + + int logoVal = sa.getResourceId(logoRes, 0); + if (logoVal != 0) { + parsingPackage.setLogo(logoVal); + } + + int bannerVal = sa.getResourceId(bannerRes, 0); + if (bannerVal != 0) { + parsingPackage.setBanner(bannerVal); + } + + TypedValue v = sa.peekValue(labelRes); + if (v != null) { + parsingPackage.setLabelRes(v.resourceId); + if (v.resourceId == 0) { + parsingPackage.setNonLocalizedLabel(v.coerceToString()); + } + } + + return parseInput.success(parsingPackage); + } + + private static ParseResult parsePermissionGroup( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws XmlPullParserException, IOException { + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + + ComponentParseUtils.ParsedPermissionGroup parsedPermissionGroup = + ComponentParseUtils.parsePermissionGroup(parsingPackage, + res, parser, outError); + + if (parsedPermissionGroup == null || outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addPermissionGroup(parsedPermissionGroup); + + return parseInput.success(parsingPackage); + } + + private static ParseResult parsePermission( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws XmlPullParserException, IOException { + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + + ComponentParseUtils.ParsedPermission parsedPermission = + ComponentParseUtils.parsePermission(parsingPackage, + res, parser, outError); + + if (parsedPermission == null || outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addPermission(parsedPermission); + + return parseInput.success(parsingPackage); + } + + private static ParseResult parsePermissionTree( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws XmlPullParserException, IOException { + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + + ComponentParseUtils.ParsedPermission parsedPermission = + ComponentParseUtils.parsePermissionTree(parsingPackage, + res, parser, outError); + + if (parsedPermission == null || outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addPermission(parsedPermission); + + return parseInput.success(parsingPackage); + } + + private static ParseResult parseUsesPermission( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + PackageParser.Callback callback + ) + throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUsesPermission); + + // Note: don't allow this value to be a reference to a resource + // that may change. + String name = sa.getNonResourceString( + R.styleable.AndroidManifestUsesPermission_name); + + int maxSdkVersion = 0; + TypedValue val = sa.peekValue( + R.styleable.AndroidManifestUsesPermission_maxSdkVersion); + if (val != null) { + if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) { + maxSdkVersion = val.data; + } + } + + final String requiredFeature = sa.getNonConfigurationString( + R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); + + final String requiredNotfeature = sa.getNonConfigurationString( + R.styleable.AndroidManifestUsesPermission_requiredNotFeature, + 0); + + sa.recycle(); + + XmlUtils.skipCurrentTag(parser); + + // Can only succeed from here on out + ParseResult success = parseInput.success(parsingPackage); + + if (name == null) { + return success; + } + + if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) { + return success; + } + + // Only allow requesting this permission if the platform supports the given feature. + if (requiredFeature != null && callback != null && !callback.hasFeature(requiredFeature)) { + return success; + } + + // Only allow requesting this permission if the platform doesn't support the given feature. + if (requiredNotfeature != null && callback != null + && callback.hasFeature(requiredNotfeature)) { + return success; + } + + if (!parsingPackage.getRequestedPermissions().contains(name)) { + parsingPackage.addRequestedPermission(name.intern()); + } else { + Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " + + name + " in package: " + parsingPackage.getPackageName() + " at: " + + parser.getPositionDescription()); + } + + return success; + } + + private static boolean parseUsesConfiguration( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + ConfigurationInfo cPref = new ConfigurationInfo(); + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUsesConfiguration); + cPref.reqTouchScreen = sa.getInt( + R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen, + Configuration.TOUCHSCREEN_UNDEFINED); + cPref.reqKeyboardType = sa.getInt( + R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType, + Configuration.KEYBOARD_UNDEFINED); + if (sa.getBoolean( + R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard, + false)) { + cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; + } + cPref.reqNavigation = sa.getInt( + R.styleable.AndroidManifestUsesConfiguration_reqNavigation, + Configuration.NAVIGATION_UNDEFINED); + if (sa.getBoolean( + R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav, + false)) { + cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; + } + sa.recycle(); + parsingPackage.addConfigPreference(cPref); + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static boolean parseUsesFeature( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + FeatureInfo fi = parseFeatureInfo(res, parser); + parsingPackage.addReqFeature(fi); + + if (fi.name == null) { + ConfigurationInfo cPref = new ConfigurationInfo(); + cPref.reqGlEsVersion = fi.reqGlEsVersion; + parsingPackage.addConfigPreference(cPref); + } + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) { + FeatureInfo fi = new FeatureInfo(); + TypedArray sa = res.obtainAttributes(attrs, + R.styleable.AndroidManifestUsesFeature); + // Note: don't allow this value to be a reference to a resource + // that may change. + fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name); + fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0); + if (fi.name == null) { + fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion, + FeatureInfo.GL_ES_VERSION_UNDEFINED); + } + if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) { + fi.flags |= FeatureInfo.FLAG_REQUIRED; + } + sa.recycle(); + return fi; + } + + private static boolean parseFeatureGroup( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + FeatureGroupInfo group = new FeatureGroupInfo(); + ArrayList<FeatureInfo> features = null; + final int innerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final String innerTagName = parser.getName(); + if (innerTagName.equals("uses-feature")) { + FeatureInfo featureInfo = parseFeatureInfo(res, parser); + // FeatureGroups are stricter and mandate that + // any <uses-feature> declared are mandatory. + featureInfo.flags |= FeatureInfo.FLAG_REQUIRED; + features = ArrayUtils.add(features, featureInfo); + } else { + Slog.w(TAG, + "Unknown element under <feature-group>: " + innerTagName + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + } + + if (features != null) { + group.features = new FeatureInfo[features.size()]; + group.features = features.toArray(group.features); + } + + parsingPackage.addFeatureGroup(group); + return true; + } + + private static ParseResult parseUsesSdk( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + if (PackageParser.SDK_VERSION > 0) { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUsesSdk); + + int minVers = 1; + String minCode = null; + int targetVers = 0; + String targetCode = null; + + TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion); + if (val != null) { + if (val.type == TypedValue.TYPE_STRING && val.string != null) { + minCode = val.string.toString(); + } else { + // If it's not a string, it's an integer. + minVers = val.data; + } + } + + val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion); + if (val != null) { + if (val.type == TypedValue.TYPE_STRING && val.string != null) { + targetCode = val.string.toString(); + if (minCode == null) { + minCode = targetCode; + } + } else { + // If it's not a string, it's an integer. + targetVers = val.data; + } + } else { + targetVers = minVers; + targetCode = minCode; + } + + sa.recycle(); + + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, + minCode, + PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, outError); + if (minSdkVersion < 0) { + return parseInput.error( + PackageManager.INSTALL_FAILED_OLDER_SDK + ); + } + + final int targetSdkVersion = PackageParser.computeTargetSdkVersion( + targetVers, + targetCode, PackageParser.SDK_CODENAMES, outError); + if (targetSdkVersion < 0) { + return parseInput.error( + PackageManager.INSTALL_FAILED_OLDER_SDK + ); + } + + parsingPackage.setMinSdkVersion(minSdkVersion) + .setTargetSdkVersion(targetSdkVersion); + } + + XmlUtils.skipCurrentTag(parser); + return parseInput.success(parsingPackage); + } + + private static boolean parseRestrictUpdateHash( + int flags, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestRestrictUpdate); + final String hash = sa.getNonConfigurationString( + R.styleable.AndroidManifestRestrictUpdate_hash, + 0); + sa.recycle(); + + if (hash != null) { + final int hashLength = hash.length(); + final byte[] hashBytes = new byte[hashLength / 2]; + for (int i = 0; i < hashLength; i += 2) { + hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16) + << 4) + + Character.digit(hash.charAt(i + 1), 16)); + } + parsingPackage.setRestrictUpdateHash(hashBytes); + } else { + parsingPackage.setRestrictUpdateHash(null); + } + } + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static ParseResult parseQueries( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (parser.getName().equals("intent")) { + String[] outError = new String[1]; + ComponentParseUtils.ParsedQueriesIntentInfo intentInfo = + ComponentParseUtils.parsedParsedQueriesIntentInfo( + parsingPackage, res, parser, outError + ); + if (intentInfo == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + Uri data = null; + String dataType = null; + String host = ""; + final int numActions = intentInfo.countActions(); + final int numSchemes = intentInfo.countDataSchemes(); + final int numTypes = intentInfo.countDataTypes(); + final int numHosts = intentInfo.getHosts().length; + if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) { + outError[0] = "intent tags must contain either an action or data."; + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + if (numActions > 1) { + outError[0] = "intent tag may have at most one action."; + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + if (numTypes > 1) { + outError[0] = "intent tag may have at most one data type."; + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + if (numSchemes > 1) { + outError[0] = "intent tag may have at most one data scheme."; + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + if (numHosts > 1) { + outError[0] = "intent tag may have at most one data host."; + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + Intent intent = new Intent(); + for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { + intent.addCategory(intentInfo.getCategory(i)); + } + if (numHosts == 1) { + host = intentInfo.getHosts()[0]; + } + if (numSchemes == 1) { + data = new Uri.Builder() + .scheme(intentInfo.getDataScheme(0)) + .authority(host) + .build(); + } + if (numTypes == 1) { + dataType = intentInfo.getDataType(0); + } + intent.setDataAndType(data, dataType); + if (numActions == 1) { + intent.setAction(intentInfo.getAction(0)); + } + parsingPackage.addQueriesIntent(intent); + } else if (parser.getName().equals("package")) { + final TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestQueriesPackage); + final String packageName = + sa.getString(R.styleable.AndroidManifestQueriesPackage_name); + if (TextUtils.isEmpty(packageName)) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Package name is missing from package tag." + ); + } + parsingPackage.addQueriesPackage(packageName.intern()); + } + } + return parseInput.success(parsingPackage); + } + + /** + * Parse the {@code application} XML tree at the current parse location in a + * <em>base APK</em> manifest. + * <p> + * When adding new features, carefully consider if they should also be + * supported by split APKs. + * + * @hide + */ + public static ParseResult parseBaseApplication( + ParseInput parseInput, + String[] separateProcesses, + PackageParser.Callback callback, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + int flags + ) throws XmlPullParserException, IOException { + final String pkgName = parsingPackage.getPackageName(); + + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + TypedArray sa = null; + + try { + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestApplication); + + + parsingPackage + .setIconRes( + sa.getResourceId(R.styleable.AndroidManifestApplication_icon, 0)) + .setRoundIconRes( + sa.getResourceId(R.styleable.AndroidManifestApplication_roundIcon, 0)); + + ParseResult result = parsePackageItemInfo( + parseInput, + parsingPackage, + "<application>", + sa, false /*nameRequired*/, + R.styleable.AndroidManifestApplication_name, + R.styleable.AndroidManifestApplication_label, + R.styleable.AndroidManifestApplication_icon, + R.styleable.AndroidManifestApplication_roundIcon, + R.styleable.AndroidManifestApplication_logo, + R.styleable.AndroidManifestApplication_banner + ); + if (!result.isSuccess()) { + return result; + } + + String name = parsingPackage.getName(); + if (name != null) { + parsingPackage.setClassName(name); + } + + String manageSpaceActivity = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_manageSpaceActivity, + Configuration.NATIVE_CONFIG_VERSION); + if (manageSpaceActivity != null) { + String manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity); + + if (manageSpaceActivityName == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Empty class name in package " + pkgName + ); + } + + parsingPackage.setManageSpaceActivityName(manageSpaceActivityName); + } + + boolean allowBackup = sa.getBoolean( + R.styleable.AndroidManifestApplication_allowBackup, true); + parsingPackage.setAllowBackup(allowBackup); + + if (allowBackup) { + // backupAgent, killAfterRestore, fullBackupContent, backupInForeground, + // and restoreAnyVersion are only relevant if backup is possible for the + // given application. + String backupAgent = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_backupAgent, + Configuration.NATIVE_CONFIG_VERSION); + if (backupAgent != null) { + String backupAgentName = buildClassName(pkgName, backupAgent); + if (backupAgentName == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Empty class name in package " + pkgName + ); + } + + if (PackageParser.DEBUG_BACKUP) { + Slog.v(TAG, "android:backupAgent = " + backupAgentName + + " from " + pkgName + "+" + backupAgent); + } + + parsingPackage.setBackupAgentName(backupAgentName); + + parsingPackage.setKillAfterRestore(sa.getBoolean( + R.styleable.AndroidManifestApplication_killAfterRestore, true)); + + parsingPackage.setRestoreAnyVersion(sa.getBoolean( + R.styleable.AndroidManifestApplication_restoreAnyVersion, false)); + + parsingPackage.setFullBackupOnly(sa.getBoolean( + R.styleable.AndroidManifestApplication_fullBackupOnly, false)); + + parsingPackage.setBackupInForeground(sa.getBoolean( + R.styleable.AndroidManifestApplication_backupInForeground, + false)); + } + + TypedValue v = sa.peekValue( + R.styleable.AndroidManifestApplication_fullBackupContent); + int fullBackupContent = 0; + + if (v != null) { + fullBackupContent = v.resourceId; + + if (v.resourceId == 0) { + if (PackageParser.DEBUG_BACKUP) { + Slog.v(TAG, "fullBackupContent specified as boolean=" + + (v.data == 0 ? "false" : "true")); + } + // "false" => -1, "true" => 0 + fullBackupContent = v.data == 0 ? -1 : 0; + } + + parsingPackage.setFullBackupContent(fullBackupContent); + } + if (PackageParser.DEBUG_BACKUP) { + Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName); + } + } + + parsingPackage + .setTheme( + sa.getResourceId(R.styleable.AndroidManifestApplication_theme, 0)) + .setDescriptionRes( + sa.getResourceId(R.styleable.AndroidManifestApplication_description, + 0)); + + if (sa.getBoolean( + R.styleable.AndroidManifestApplication_persistent, + false)) { + // Check if persistence is based on a feature being present + final String requiredFeature = sa.getNonResourceString(R.styleable + .AndroidManifestApplication_persistentWhenFeatureAvailable); + parsingPackage.setPersistent(requiredFeature == null + || callback.hasFeature(requiredFeature)); + } + + boolean requiredForAllUsers = sa.getBoolean( + R.styleable.AndroidManifestApplication_requiredForAllUsers, + false); + parsingPackage.setRequiredForAllUsers(requiredForAllUsers); + + String restrictedAccountType = sa.getString(R.styleable + .AndroidManifestApplication_restrictedAccountType); + if (restrictedAccountType != null && restrictedAccountType.length() > 0) { + parsingPackage.setRestrictedAccountType(restrictedAccountType); + } + + String requiredAccountType = sa.getString(R.styleable + .AndroidManifestApplication_requiredAccountType); + if (requiredAccountType != null && requiredAccountType.length() > 0) { + parsingPackage.setRequiredAccountType(requiredAccountType); + } + + parsingPackage.setForceQueryable( + sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false) + ); + + boolean debuggable = sa.getBoolean( + R.styleable.AndroidManifestApplication_debuggable, + false + ); + + parsingPackage.setDebuggable(debuggable); + + if (debuggable) { + // Debuggable implies profileable + parsingPackage.setProfileableByShell(true); + } + + parsingPackage.setVmSafeMode(sa.getBoolean( + R.styleable.AndroidManifestApplication_vmSafeMode, false)); + + boolean baseHardwareAccelerated = sa.getBoolean( + R.styleable.AndroidManifestApplication_hardwareAccelerated, + parsingPackage.getTargetSdkVersion() + >= Build.VERSION_CODES.ICE_CREAM_SANDWICH); + parsingPackage.setBaseHardwareAccelerated(baseHardwareAccelerated); + + parsingPackage.setHasCode(sa.getBoolean( + R.styleable.AndroidManifestApplication_hasCode, true)); + + parsingPackage.setAllowTaskReparenting(sa.getBoolean( + R.styleable.AndroidManifestApplication_allowTaskReparenting, false)); + + parsingPackage.setAllowClearUserData(sa.getBoolean( + R.styleable.AndroidManifestApplication_allowClearUserData, true)); + + parsingPackage.setTestOnly(sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_testOnly, + false)); + + parsingPackage.setLargeHeap(sa.getBoolean( + R.styleable.AndroidManifestApplication_largeHeap, false)); + + parsingPackage.setUsesCleartextTraffic(sa.getBoolean( + R.styleable.AndroidManifestApplication_usesCleartextTraffic, + parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.P)); + + parsingPackage.setSupportsRtl(sa.getBoolean( + R.styleable.AndroidManifestApplication_supportsRtl, + false /* default is no RTL support*/)); + + parsingPackage.setMultiArch(sa.getBoolean( + R.styleable.AndroidManifestApplication_multiArch, false)); + + parsingPackage.setExtractNativeLibs(sa.getBoolean( + R.styleable.AndroidManifestApplication_extractNativeLibs, true)); + + parsingPackage.setUseEmbeddedDex(sa.getBoolean( + R.styleable.AndroidManifestApplication_useEmbeddedDex, false)); + + parsingPackage.setDefaultToDeviceProtectedStorage(sa.getBoolean( + R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, + false)); + + parsingPackage.setDirectBootAware(sa.getBoolean( + R.styleable.AndroidManifestApplication_directBootAware, false)); + + if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) { + parsingPackage.setActivitiesResizeModeResizeable(sa.getBoolean( + R.styleable.AndroidManifestApplication_resizeableActivity, true)); + } else { + parsingPackage.setActivitiesResizeModeResizeableViaSdkVersion( + parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.N); + } + + parsingPackage.setAllowClearUserDataOnFailedRestore(sa.getBoolean( + R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, + true)); + + + parsingPackage.setAllowAudioPlaybackCapture(sa.getBoolean( + R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, + parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q)); + + parsingPackage.setRequestLegacyExternalStorage(sa.getBoolean( + R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, + parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q)); + + parsingPackage + .setMaxAspectRatio( + sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0)) + .setMinAspectRatio( + sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0)) + .setNetworkSecurityConfigRes(sa.getResourceId( + R.styleable.AndroidManifestApplication_networkSecurityConfig, 0)) + .setCategory(sa.getInt(R.styleable.AndroidManifestApplication_appCategory, + ApplicationInfo.CATEGORY_UNDEFINED)); + + String str; + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_permission, 0); + parsingPackage.setPermission((str != null && str.length() > 0) ? str.intern() : null); + + if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) { + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_taskAffinity, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + str = sa.getNonResourceString( + R.styleable.AndroidManifestApplication_taskAffinity); + } + String packageName = parsingPackage.getPackageName(); + String taskAffinity = PackageParser.buildTaskAffinityName(packageName, + packageName, + str, outError); + + if (outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.setTaskAffinity(taskAffinity); + String factory = sa.getNonResourceString( + R.styleable.AndroidManifestApplication_appComponentFactory); + if (factory != null) { + String appComponentFactory = buildClassName(packageName, factory); + if (appComponentFactory == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Empty class name in package " + pkgName + ); + } + + parsingPackage.setAppComponentFactory(appComponentFactory); + } + + parsingPackage.setUsesNonSdkApi(sa.getBoolean( + R.styleable.AndroidManifestApplication_usesNonSdkApi, false)); + + parsingPackage.setHasFragileUserData(sa.getBoolean( + R.styleable.AndroidManifestApplication_hasFragileUserData, false)); + + CharSequence pname; + if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) { + pname = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_process, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + pname = sa.getNonResourceString( + R.styleable.AndroidManifestApplication_process); + } + String processName = PackageParser.buildProcessName(packageName, null, pname, flags, + separateProcesses, outError); + + if (outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage + .setProcessName(processName) + .setEnabled( + sa.getBoolean(R.styleable.AndroidManifestApplication_enabled, + true)); + + parsingPackage.setIsGame(sa.getBoolean( + R.styleable.AndroidManifestApplication_isGame, false)); + + boolean cantSaveState = sa.getBoolean( + R.styleable.AndroidManifestApplication_cantSaveState, false); + parsingPackage.setCantSaveState(cantSaveState); + if (cantSaveState) { + // A heavy-weight application can not be in a custom process. + // We can do direct compare because we intern all strings. + if (processName != null && !processName.equals(packageName)) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "cantSaveState applications can not use custom processes" + ); + } + } + + String classLoaderName = sa.getString( + R.styleable.AndroidManifestApplication_classLoader); + parsingPackage + .setUiOptions(sa.getInt(R.styleable.AndroidManifestApplication_uiOptions, 0)) + .setClassLoaderName(classLoaderName) + .setZygotePreloadName( + sa.getString(R.styleable.AndroidManifestApplication_zygotePreloadName)); + + if (classLoaderName != null + && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Invalid class loader name: " + classLoaderName + ); + } + } finally { + if (sa != null) { + sa.recycle(); + } + } + + final int innerDepth = parser.getDepth(); + int type; + boolean hasActivityOrder = false; + boolean hasReceiverOrder = false; + boolean hasServiceOrder = false; + + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + switch (tagName) { + case "activity": + ComponentParseUtils.ParsedActivity activity = + ComponentParseUtils.parseActivity(separateProcesses, + parsingPackage, + res, parser, flags, + outError, false, + parsingPackage.isBaseHardwareAccelerated()); + if (activity == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + hasActivityOrder |= (activity.order != 0); + parsingPackage.addActivity(activity); + break; + case "receiver": + activity = ComponentParseUtils.parseActivity(separateProcesses, + parsingPackage, + res, parser, + flags, outError, + true, false); + if (activity == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + hasReceiverOrder |= (activity.order != 0); + parsingPackage.addReceiver(activity); + break; + case "service": + ComponentParseUtils.ParsedService s = ComponentParseUtils.parseService( + separateProcesses, + parsingPackage, + res, parser, flags, + outError); + if (s == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + hasServiceOrder |= (s.order != 0); + parsingPackage.addService(s); + break; + case "provider": + ComponentParseUtils.ParsedProvider p = ComponentParseUtils.parseProvider( + separateProcesses, + parsingPackage, + res, parser, flags, + outError + ); + if (p == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addProvider(p); + break; + case "activity-alias": + activity = ComponentParseUtils.parseActivityAlias( + parsingPackage, + res, + parser, + outError + ); + if (activity == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + hasActivityOrder |= (activity.order != 0); + parsingPackage.addActivity(activity); + break; + case "meta-data": + // note: application meta-data is stored off to the side, so it can + // remain null in the primary copy (we like to avoid extra copies because + // it can be large) + Bundle appMetaData = parseMetaData(parsingPackage, res, parser, + parsingPackage.getAppMetaData(), + outError); + if (appMetaData == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.setAppMetaData(appMetaData); + break; + case "static-library": + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestStaticLibrary); + + // Note: don't allow this value to be a reference to a resource + // that may change. + String lname = sa.getNonResourceString( + R.styleable.AndroidManifestStaticLibrary_name); + final int version = sa.getInt( + R.styleable.AndroidManifestStaticLibrary_version, -1); + final int versionMajor = sa.getInt( + R.styleable.AndroidManifestStaticLibrary_versionMajor, + 0); + + sa.recycle(); + + // Since the app canot run without a static lib - fail if malformed + if (lname == null || version < 0) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad static-library declaration name: " + lname + + " version: " + version + ); + } + + if (parsingPackage.getSharedUserId() != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, + "sharedUserId not allowed in static shared library" + ); + } + + if (parsingPackage.getStaticSharedLibName() != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Multiple static-shared libs for package " + pkgName + ); + } + + parsingPackage.setStaticSharedLibName(lname.intern()); + if (version >= 0) { + parsingPackage.setStaticSharedLibVersion( + PackageInfo.composeLongVersionCode(versionMajor, version)); + } else { + parsingPackage.setStaticSharedLibVersion(version); + } + parsingPackage.setStaticSharedLibrary(true); + + XmlUtils.skipCurrentTag(parser); + + break; + case "library": + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestLibrary); + + // Note: don't allow this value to be a reference to a resource + // that may change. + lname = sa.getNonResourceString( + R.styleable.AndroidManifestLibrary_name); + + sa.recycle(); + + if (lname != null) { + lname = lname.intern(); + if (!ArrayUtils.contains(parsingPackage.getLibraryNames(), lname)) { + parsingPackage.addLibraryName(lname); + } + } + + XmlUtils.skipCurrentTag(parser); + + break; + case "uses-static-library": + ParseResult parseResult = parseUsesStaticLibrary(parseInput, parsingPackage, + res, parser); + if (!parseResult.isSuccess()) { + return parseResult; + } + break; + case "uses-library": + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUsesLibrary); + + // Note: don't allow this value to be a reference to a resource + // that may change. + lname = sa.getNonResourceString( + R.styleable.AndroidManifestUsesLibrary_name); + boolean req = sa.getBoolean( + R.styleable.AndroidManifestUsesLibrary_required, + true); + + sa.recycle(); + + if (lname != null) { + lname = lname.intern(); + if (req) { + parsingPackage.addUsesLibrary(lname); + } else { + parsingPackage.addUsesOptionalLibrary(lname); + } + } + + XmlUtils.skipCurrentTag(parser); + + break; + case "uses-package": + // Dependencies for app installers; we don't currently try to + // enforce this. + XmlUtils.skipCurrentTag(parser); + break; + case "profileable": + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestProfileable); + if (sa.getBoolean( + R.styleable.AndroidManifestProfileable_shell, false)) { + parsingPackage.setProfileableByShell(true); + } + XmlUtils.skipCurrentTag(parser); + + default: + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <application>: " + tagName + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad element under <application>: " + tagName + ); + } + } + } + + if (TextUtils.isEmpty(parsingPackage.getStaticSharedLibName())) { + // Add a hidden app detail activity to normal apps which forwards user to App Details + // page. + ComponentParseUtils.ParsedActivity a = generateAppDetailsHiddenActivity( + parsingPackage, + outError + ); + // Ignore errors here + parsingPackage.addActivity(a); + } + + if (hasActivityOrder) { + parsingPackage.sortActivities(); + } + if (hasReceiverOrder) { + parsingPackage.sortReceivers(); + } + if (hasServiceOrder) { + parsingPackage.sortServices(); + } + // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after + // every activity info has had a chance to set it from its attributes. + setMaxAspectRatio(parsingPackage); + setMinAspectRatio(parsingPackage, callback); + + parsingPackage.setHasDomainUrls(hasDomainURLs(parsingPackage)); + + return parseInput.success(parsingPackage); + } + + private static ParseResult parseUsesStaticLibrary( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUsesStaticLibrary); + + // Note: don't allow this value to be a reference to a resource that may change. + String lname = sa.getNonResourceString( + R.styleable.AndroidManifestUsesLibrary_name); + final int version = sa.getInt( + R.styleable.AndroidManifestUsesStaticLibrary_version, -1); + String certSha256Digest = sa.getNonResourceString(com.android.internal.R.styleable + .AndroidManifestUsesStaticLibrary_certDigest); + sa.recycle(); + + // Since an APK providing a static shared lib can only provide the lib - fail if malformed + if (lname == null || version < 0 || certSha256Digest == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad uses-static-library declaration name: " + lname + " version: " + + version + " certDigest" + certSha256Digest + ); + } + + // Can depend only on one version of the same library + List<String> usesStaticLibraries = parsingPackage.getUsesStaticLibraries(); + if (usesStaticLibraries != null && usesStaticLibraries.contains(lname)) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Depending on multiple versions of static library " + lname + ); + } + + lname = lname.intern(); + // We allow ":" delimiters in the SHA declaration as this is the format + // emitted by the certtool making it easy for developers to copy/paste. + certSha256Digest = certSha256Digest.replace(":", "").toLowerCase(); + + // Fot apps targeting O-MR1 we require explicit enumeration of all certs. + String[] additionalCertSha256Digests = EmptyArray.STRING; + if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) { + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError); + if (additionalCertSha256Digests == null || outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + } else { + XmlUtils.skipCurrentTag(parser); + } + + final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1]; + certSha256Digests[0] = certSha256Digest; + System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, + 1, additionalCertSha256Digests.length); + + parsingPackage.addUsesStaticLibrary(lname) + .addUsesStaticLibraryVersion(version) + .addUsesStaticLibraryCertDigests(certSha256Digests); + + return parseInput.success(parsingPackage); + } + + private static String[] parseAdditionalCertificates( + Resources resources, + XmlResourceParser parser, + String[] outError + ) throws XmlPullParserException, IOException { + String[] certSha256Digests = EmptyArray.STRING; + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final String nodeName = parser.getName(); + if (nodeName.equals("additional-certificate")) { + final TypedArray sa = resources.obtainAttributes(parser, com.android.internal. + R.styleable.AndroidManifestAdditionalCertificate); + String certSha256Digest = sa.getNonResourceString(com.android.internal. + R.styleable.AndroidManifestAdditionalCertificate_certDigest); + sa.recycle(); + + if (TextUtils.isEmpty(certSha256Digest)) { + outError[0] = "Bad additional-certificate declaration with empty" + + " certDigest:" + certSha256Digest; + XmlUtils.skipCurrentTag(parser); + sa.recycle(); + return null; + } + + // We allow ":" delimiters in the SHA declaration as this is the format + // emitted by the certtool making it easy for developers to copy/paste. + certSha256Digest = certSha256Digest.replace(":", "").toLowerCase(); + certSha256Digests = ArrayUtils.appendElement(String.class, + certSha256Digests, certSha256Digest); + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + return certSha256Digests; + } + + /** + * Generate activity object that forwards user to App Details page automatically. + * This activity should be invisible to user and user should not know or see it. + * + * @hide + */ + @NonNull + private static ComponentParseUtils.ParsedActivity generateAppDetailsHiddenActivity( + ParsingPackage parsingPackage, + String[] outError + ) { + String packageName = parsingPackage.getPackageName(); + String processName = parsingPackage.getProcessName(); + boolean hardwareAccelerated = parsingPackage.isBaseHardwareAccelerated(); + int uiOptions = parsingPackage.getUiOptions(); + + // Build custom App Details activity info instead of parsing it from xml + ComponentParseUtils.ParsedActivity activity = new ComponentParseUtils.ParsedActivity(); + activity.setPackageName(packageName); + + activity.theme = android.R.style.Theme_NoDisplay; + activity.exported = true; + activity.className = PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME; + activity.setProcessName(processName, processName); + activity.uiOptions = uiOptions; + activity.taskAffinity = PackageParser.buildTaskAffinityName(packageName, + packageName, + ":app_details", outError); + activity.enabled = true; + activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE; + activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic(); + activity.configChanges = PackageParser.getActivityConfigChanges(0, 0); + activity.softInputMode = 0; + activity.persistableMode = ActivityInfo.PERSIST_NEVER; + activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; + activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE; + activity.lockTaskLaunchMode = 0; + activity.directBootAware = false; + activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED; + activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT; + if (hardwareAccelerated) { + activity.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED; + } + + return activity; + } + + /** + * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI + */ + private static boolean hasDomainURLs( + ParsingPackage parsingPackage) { + final List<ComponentParseUtils.ParsedActivity> activities = parsingPackage.getActivities(); + final int countActivities = activities.size(); + for (int n = 0; n < countActivities; n++) { + ComponentParseUtils.ParsedActivity activity = activities.get(n); + List<ComponentParseUtils.ParsedActivityIntentInfo> filters = activity.intents; + if (filters == null) continue; + final int countFilters = filters.size(); + for (int m = 0; m < countFilters; m++) { + ComponentParseUtils.ParsedActivityIntentInfo aii = filters.get(m); + if (!aii.hasAction(Intent.ACTION_VIEW)) continue; + if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue; + if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) || + aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) { + return true; + } + } + } + return false; + } + + /** + * Sets the max aspect ratio of every child activity that doesn't already have an aspect + * ratio set. + */ + private static void setMaxAspectRatio( + ParsingPackage parsingPackage) { + // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater. + // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD. + float maxAspectRatio = parsingPackage.getTargetSdkVersion() < O + ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; + + float packageMaxAspectRatio = parsingPackage.getMaxAspectRatio(); + if (packageMaxAspectRatio != 0) { + // Use the application max aspect ration as default if set. + maxAspectRatio = packageMaxAspectRatio; + } else { + Bundle appMetaData = parsingPackage.getAppMetaData(); + if (appMetaData != null && appMetaData.containsKey( + PackageParser.METADATA_MAX_ASPECT_RATIO)) { + maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO, + maxAspectRatio); + } + } + + if (parsingPackage.getActivities() != null) { + for (ComponentParseUtils.ParsedActivity activity : parsingPackage.getActivities()) { + // If the max aspect ratio for the activity has already been set, skip. + if (activity.hasMaxAspectRatio()) { + continue; + } + + // By default we prefer to use a values defined on the activity directly than values + // defined on the application. We do not check the styled attributes on the activity + // as it would have already been set when we processed the activity. We wait to + // process the meta data here since this method is called at the end of processing + // the application and all meta data is guaranteed. + final float activityAspectRatio = activity.metaData != null + ? activity.metaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO, + maxAspectRatio) + : maxAspectRatio; + + activity.setMaxAspectRatio(activity.resizeMode, activityAspectRatio); + } + } + } + + /** + * Sets the min aspect ratio of every child activity that doesn't already have an aspect + * ratio set. + */ + private static void setMinAspectRatio( + ParsingPackage parsingPackage, + PackageParser.Callback callback + ) { + final float minAspectRatio; + float packageMinAspectRatio = parsingPackage.getMinAspectRatio(); + if (packageMinAspectRatio != 0) { + // Use the application max aspect ration as default if set. + minAspectRatio = packageMinAspectRatio; + } else { + // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater. + // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD, + // except for watches which always supported 1:1. + minAspectRatio = parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q + ? 0 + : (callback != null && callback.hasFeature(FEATURE_WATCH)) + ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH + : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO; + } + + if (parsingPackage.getActivities() != null) { + for (ComponentParseUtils.ParsedActivity activity : parsingPackage.getActivities()) { + if (activity.hasMinAspectRatio()) { + continue; + } + activity.setMinAspectRatio(activity.resizeMode, minAspectRatio); + } + } + } + + private static ParseResult parseOverlay( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay); + String target = sa.getString( + R.styleable.AndroidManifestResourceOverlay_targetPackage); + String targetName = sa.getString( + R.styleable.AndroidManifestResourceOverlay_targetName); + String category = sa.getString( + R.styleable.AndroidManifestResourceOverlay_category); + int priority = sa.getInt(R.styleable.AndroidManifestResourceOverlay_priority, + 0); + boolean isStatic = sa.getBoolean( + R.styleable.AndroidManifestResourceOverlay_isStatic, false); + + if (target == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "<overlay> does not specify a target package" + ); + } + + if (priority < 0 || priority > 9999) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "<overlay> priority must be between 0 and 9999" + ); + } + + // check to see if overlay should be excluded based on system property condition + String propName = sa.getString( + R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName); + String propValue = sa.getString( + R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue); + if (!checkOverlayRequiredSystemProperty(propName, propValue)) { + Slog.i(TAG, "Skipping target and overlay pair " + target + " and " + + parsingPackage.getBaseCodePath() + + ": overlay ignored due to required system property: " + + propName + " with value: " + propValue); + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Skipping target and overlay pair " + target + " and " + + parsingPackage.getBaseCodePath() + + ": overlay ignored due to required system property: " + + propName + " with value: " + propValue + ); + } + + parsingPackage + .setIsOverlay(true) + .setOverlayTarget(target) + .setOverlayTargetName(targetName) + .setOverlayCategory(category) + .setOverlayPriority(priority) + .setOverlayIsStatic(isStatic); + + sa.recycle(); + + XmlUtils.skipCurrentTag(parser); + return parseInput.success(parsingPackage); + } + + private static boolean parseProtectedBroadcast( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestProtectedBroadcast); + + // Note: don't allow this value to be a reference to a resource + // that may change. + String name = sa.getNonResourceString(R.styleable.AndroidManifestProtectedBroadcast_name); + + sa.recycle(); + + if (name != null) { + parsingPackage.addProtectedBroadcast(name); + } + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static boolean parseSupportScreens( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestSupportsScreens); + + int requiresSmallestWidthDp = sa.getInteger( + R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, + 0); + int compatibleWidthLimitDp = sa.getInteger( + R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, + 0); + int largestWidthLimitDp = sa.getInteger( + R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, + 0); + + // This is a trick to get a boolean and still able to detect + // if a value was actually set. + parsingPackage + .setSupportsSmallScreens( + sa.getInteger(R.styleable.AndroidManifestSupportsScreens_smallScreens, 1)) + .setSupportsNormalScreens( + sa.getInteger(R.styleable.AndroidManifestSupportsScreens_normalScreens, 1)) + .setSupportsLargeScreens( + sa.getInteger(R.styleable.AndroidManifestSupportsScreens_largeScreens, 1)) + .setSupportsXLargeScreens( + sa.getInteger(R.styleable.AndroidManifestSupportsScreens_xlargeScreens, 1)) + .setResizeable( + sa.getInteger(R.styleable.AndroidManifestSupportsScreens_resizeable, 1)) + .setAnyDensity( + sa.getInteger(R.styleable.AndroidManifestSupportsScreens_anyDensity, 1)) + .setRequiresSmallestWidthDp(requiresSmallestWidthDp) + .setCompatibleWidthLimitDp(compatibleWidthLimitDp) + .setLargestWidthLimitDp(largestWidthLimitDp); + + sa.recycle(); + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static ParseResult parseInstrumentation( + ParseInput parseInput, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws XmlPullParserException, IOException { + // TODO(b/135203078): Remove, replace with ParseResult + String[] outError = new String[1]; + + ComponentParseUtils.ParsedInstrumentation parsedInstrumentation = + ComponentParseUtils.parseInstrumentation(parsingPackage, + res, parser, outError); + + if (parsedInstrumentation == null || outError[0] != null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.addInstrumentation(parsedInstrumentation); + + return parseInput.success(parsingPackage); + } + + private static boolean parseOriginalPackage( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestOriginalPackage); + + String orig = sa.getNonConfigurationString( + R.styleable.AndroidManifestOriginalPackage_name, + 0); + if (!parsingPackage.getPackageName().equals(orig)) { + if (parsingPackage.getOriginalPackages() == null) { + parsingPackage.setRealPackage(parsingPackage.getPackageName()); + } + parsingPackage.addOriginalPackage(orig); + } + + sa.recycle(); + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static boolean parseAdoptPermissions( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestOriginalPackage); + + String name = sa.getNonConfigurationString( + R.styleable.AndroidManifestOriginalPackage_name, + 0); + + sa.recycle(); + + if (name != null) { + parsingPackage.addAdoptPermission(name); + } + + XmlUtils.skipCurrentTag(parser); + return true; + } + + private static void convertNewPermissions( + ParsingPackage packageToParse) { + final int NP = PackageParser.NEW_PERMISSIONS.length; + StringBuilder newPermsMsg = null; + for (int ip = 0; ip < NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (packageToParse.getTargetSdkVersion() >= npi.sdkVersion) { + break; + } + if (!packageToParse.getRequestedPermissions().contains(npi.name)) { + if (newPermsMsg == null) { + newPermsMsg = new StringBuilder(128); + newPermsMsg.append(packageToParse.getPackageName()); + newPermsMsg.append(": compat added "); + } else { + newPermsMsg.append(' '); + } + newPermsMsg.append(npi.name); + packageToParse.addRequestedPermission(npi.name); + packageToParse.addImplicitPermission(npi.name); + } + } + if (newPermsMsg != null) { + Slog.i(TAG, newPermsMsg.toString()); + } + } + + private static void convertSplitPermissions(ParsingPackage packageToParse) { + List<SplitPermissionInfoParcelable> splitPermissions; + + try { + splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + final int listSize = splitPermissions.size(); + for (int is = 0; is < listSize; is++) { + final SplitPermissionInfoParcelable spi = splitPermissions.get(is); + List<String> requestedPermissions = packageToParse.getRequestedPermissions(); + if (packageToParse.getTargetSdkVersion() >= spi.getTargetSdk() + || !requestedPermissions.contains(spi.getSplitPermission())) { + continue; + } + final List<String> newPerms = spi.getNewPermissions(); + for (int in = 0; in < newPerms.size(); in++) { + final String perm = newPerms.get(in); + if (!requestedPermissions.contains(perm)) { + packageToParse.addRequestedPermission(perm); + packageToParse.addImplicitPermission(perm); + } + } + } + } + + private static boolean checkOverlayRequiredSystemProperty(String propName, String propValue) { + if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) { + if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) { + // malformed condition - incomplete + Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName + + "=" + propValue + "' - require both requiredSystemPropertyName" + + " AND requiredSystemPropertyValue to be specified."); + return false; + } + // no valid condition set - so no exclusion criteria, overlay will be included. + return true; + } + + // check property value - make sure it is both set and equal to expected value + final String currValue = SystemProperties.get(propName); + return (currValue != null && currValue.equals(propValue)); + } + + /** + * This is a pre-density application which will get scaled - instead of being pixel perfect. + * This type of application is not resizable. + * + * @param parsingPackage The package which needs to be marked as unresizable. + */ + private static void adjustPackageToBeUnresizeableAndUnpipable( + ParsingPackage parsingPackage) { + if (parsingPackage.getActivities() != null) { + for (ComponentParseUtils.ParsedActivity a : parsingPackage.getActivities()) { + a.resizeMode = RESIZE_MODE_UNRESIZEABLE; + a.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE; + } + } + } + + private static String validateName(String name, boolean requireSeparator, + boolean requireFilename) { + final int N = name.length(); + boolean hasSep = false; + boolean front = true; + for (int i = 0; i < N; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + } + if (c == '.') { + hasSep = true; + front = true; + continue; + } + return "bad character '" + c + "'"; + } + if (requireFilename && !FileUtils.isValidExtFilename(name)) { + return "Invalid filename"; + } + return hasSep || !requireSeparator + ? null : "must have at least one '.' separator"; + } + + public static Bundle parseMetaData( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, Bundle data, String[] outError) + throws XmlPullParserException, IOException { + + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestMetaData); + + if (data == null) { + data = new Bundle(); + } + + String name = sa.getNonConfigurationString( + R.styleable.AndroidManifestMetaData_name, 0); + if (name == null) { + outError[0] = "<meta-data> requires an android:name attribute"; + sa.recycle(); + return null; + } + + name = name.intern(); + + TypedValue v = sa.peekValue( + R.styleable.AndroidManifestMetaData_resource); + if (v != null && v.resourceId != 0) { + //Slog.i(TAG, "Meta data ref " + name + ": " + v); + data.putInt(name, v.resourceId); + } else { + v = sa.peekValue( + R.styleable.AndroidManifestMetaData_value); + //Slog.i(TAG, "Meta data " + name + ": " + v); + if (v != null) { + if (v.type == TypedValue.TYPE_STRING) { + CharSequence cs = v.coerceToString(); + data.putString(name, cs != null ? cs.toString() : null); + } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { + data.putBoolean(name, v.data != 0); + } else if (v.type >= TypedValue.TYPE_FIRST_INT + && v.type <= TypedValue.TYPE_LAST_INT) { + data.putInt(name, v.data); + } else if (v.type == TypedValue.TYPE_FLOAT) { + data.putFloat(name, v.getFloat()); + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, + "<meta-data> only supports string, integer, float, color, " + + "boolean, and resource reference types: " + + parser.getName() + " at " + + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } else { + outError[0] = + "<meta-data> only supports string, integer, float, color, " + + "boolean, and resource reference types"; + data = null; + } + } + } else { + outError[0] = "<meta-data> requires an android:value or android:resource attribute"; + data = null; + } + } + + sa.recycle(); + + XmlUtils.skipCurrentTag(parser); + + return data; + } + + /** + * Collect certificates from all the APKs described in the given package, + * populating {@link AndroidPackageWrite#setSigningDetails(SigningDetails)}. Also asserts that + * all APK contents are signed correctly and consistently. + */ + public static void collectCertificates(AndroidPackage pkg, boolean skipVerify) + throws PackageParserException { + pkg.mutate().setSigningDetails(SigningDetails.UNKNOWN); + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); + try { + pkg.mutate().setSigningDetails(collectCertificates( + pkg.getBaseCodePath(), + skipVerify, + pkg.isStaticSharedLibrary(), + pkg.getSigningDetails() + )); + + String[] splitCodePaths = pkg.getSplitCodePaths(); + if (!ArrayUtils.isEmpty(splitCodePaths)) { + for (int i = 0; i < splitCodePaths.length; i++) { + pkg.mutate().setSigningDetails(collectCertificates( + splitCodePaths[i], + skipVerify, + pkg.isStaticSharedLibrary(), + pkg.getSigningDetails() + )); + } + } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + public static SigningDetails collectCertificates( + String baseCodePath, + boolean skipVerify, + boolean isStaticSharedLibrary, + @NonNull SigningDetails existingSigningDetails + ) throws PackageParserException { + int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR; + if (isStaticSharedLibrary) { + // must use v2 signing scheme + minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2; + } + SigningDetails verified; + if (skipVerify) { + // systemDir APKs are already trusted, save time by not verifying + verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification( + baseCodePath, minSignatureScheme); + } else { + verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme); + } + + // Verify that entries are signed consistently with the first pkg + // we encountered. Note that for splits, certificates may have + // already been populated during an earlier parse of a base APK. + if (existingSigningDetails == SigningDetails.UNKNOWN) { + return verified; + } else { + if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) { + throw new PackageParserException( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, + baseCodePath + " has mismatched certificates"); + } + + return existingSigningDetails; + } + } + + @Nullable + public static String buildClassName(String pkg, CharSequence clsSeq) { + if (clsSeq == null || clsSeq.length() <= 0) { + return null; + } + String cls = clsSeq.toString(); + char c = cls.charAt(0); + if (c == '.') { + return pkg + cls; + } + if (cls.indexOf('.') < 0) { + StringBuilder b = new StringBuilder(pkg); + b.append('.'); + b.append(cls); + return b.toString(); + } + return cls; + } + + public interface ParseInput { + ParseResult success(ParsingPackage result); + + ParseResult error(int parseError); + + ParseResult error(int parseError, String errorMessage); + } + + public static class ParseResult implements ParseInput { + + private static final boolean DEBUG_FILL_STACK_TRACE = false; + + private ParsingPackage result; + + private int parseError; + private String errorMessage; + + public ParseInput reset() { + this.result = null; + this.parseError = PackageManager.INSTALL_SUCCEEDED; + this.errorMessage = null; + return this; + } + + @Override + public ParseResult success(ParsingPackage result) { + if (parseError != PackageManager.INSTALL_SUCCEEDED || errorMessage != null) { + throw new IllegalStateException("Cannot set to success after set to error"); + } + this.result = result; + return this; + } + + @Override + public ParseResult error(int parseError) { + return error(parseError, null); + } + + @Override + public ParseResult error(int parseError, String errorMessage) { + this.parseError = parseError; + this.errorMessage = errorMessage; + + if (DEBUG_FILL_STACK_TRACE) { + this.errorMessage += Arrays.toString(new Exception().getStackTrace()); + } + + return this; + } + + public ParsingPackage getResultAndNull() { + ParsingPackage result = this.result; + this.result = null; + return result; + } + + public boolean isSuccess() { + return parseError == PackageManager.INSTALL_SUCCEEDED; + } + + public int getParseError() { + return parseError; + } + + public String getErrorMessage() { + return errorMessage; + } + } +} diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java new file mode 100644 index 000000000000..5364313f5760 --- /dev/null +++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java @@ -0,0 +1,3250 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; + +import android.annotation.CallSuper; +import android.annotation.UnsupportedAppUsage; +import android.app.ActivityTaskManager; +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PathPermission; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Slog; +import android.util.TypedValue; +import android.view.Gravity; + +import com.android.internal.R; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +/** + * TODO(b/135203078): Move the inner classes out to separate files. + * TODO(b/135203078): Expose inner classes as immutable through interface methods. + * + * @hide + */ +public class ComponentParseUtils { + + private static final String TAG = ApkParseUtils.TAG; + + // TODO(b/135203078): None of this class's subclasses do anything. Remove in favor of base? + public static class ParsedIntentInfo extends IntentFilter { + + /** + * <p> + * Implementation note: The serialized form for the intent list also contains the name + * of the concrete class that's stored in the list, and assumes that every element of the + * list is of the same type. This is very similar to the original parcelable mechanism. + * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable + * and is public API. It also declares Parcelable related methods as final which means + * we can't extend them. The approach of using composition instead of inheritance leads to + * a large set of cascading changes in the PackageManagerService, which seem undesirable. + * + * <p> + * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up + * to make sure their owner fields are consistent. See {@code fixupOwner}. + */ + public static void writeIntentsList(List<? extends ParsedIntentInfo> list, Parcel out, + int flags) { + if (list == null) { + out.writeInt(-1); + return; + } + + final int size = list.size(); + out.writeInt(size); + + // Don't bother writing the component name if the list is empty. + if (size > 0) { + ParsedIntentInfo info = list.get(0); + out.writeString(info.getClass().getName()); + + for (int i = 0; i < size; i++) { + list.get(i).writeIntentInfoToParcel(out, flags); + } + } + } + + public static <T extends ParsedIntentInfo> ArrayList<T> createIntentsList(Parcel in) { + int size = in.readInt(); + if (size == -1) { + return null; + } + + if (size == 0) { + return new ArrayList<>(0); + } + + String className = in.readString(); + final ArrayList<T> intentsList; + try { + final Class<T> cls = (Class<T>) Class.forName(className); + final Constructor<T> cons = cls.getConstructor(Parcel.class); + + intentsList = new ArrayList<>(size); + for (int i = 0; i < size; ++i) { + intentsList.add(cons.newInstance(in)); + } + } catch (ReflectiveOperationException ree) { + throw new AssertionError("Unable to construct intent list for: " + + className, ree); + } + + return intentsList; + } + + protected String packageName; + protected final String className; + + public boolean hasDefault; + public int labelRes; + public CharSequence nonLocalizedLabel; + public int icon; + + protected List<String> rawDataTypes; + + public void addRawDataType(String dataType) throws MalformedMimeTypeException { + if (rawDataTypes == null) { + rawDataTypes = new ArrayList<>(); + } + + rawDataTypes.add(dataType); + addDataType(dataType); + } + + public ParsedIntentInfo(String packageName, String className) { + this.packageName = packageName; + this.className = className; + } + + public ParsedIntentInfo(Parcel in) { + super(in); + packageName = in.readString(); + className = in.readString(); + hasDefault = (in.readInt() == 1); + labelRes = in.readInt(); + nonLocalizedLabel = in.readCharSequence(); + icon = in.readInt(); + } + + public void writeIntentInfoToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(packageName); + dest.writeString(className); + dest.writeInt(hasDefault ? 1 : 0); + dest.writeInt(labelRes); + dest.writeCharSequence(nonLocalizedLabel); + dest.writeInt(icon); + } + + public String getPackageName() { + return packageName; + } + + public String getClassName() { + return className; + } + } + + public static class ParsedActivityIntentInfo extends ParsedIntentInfo { + + public ParsedActivityIntentInfo(String packageName, String className) { + super(packageName, className); + } + + public ParsedActivityIntentInfo(Parcel in) { + super(in); + } + + public static final Creator<ParsedActivityIntentInfo> CREATOR = + new Creator<ParsedActivityIntentInfo>() { + @Override + public ParsedActivityIntentInfo createFromParcel(Parcel source) { + return new ParsedActivityIntentInfo(source); + } + + @Override + public ParsedActivityIntentInfo[] newArray(int size) { + return new ParsedActivityIntentInfo[size]; + } + }; + } + + public static class ParsedServiceIntentInfo extends ParsedIntentInfo { + + public ParsedServiceIntentInfo(String packageName, String className) { + super(packageName, className); + } + + public ParsedServiceIntentInfo(Parcel in) { + super(in); + } + + public static final Creator<ParsedServiceIntentInfo> CREATOR = + new Creator<ParsedServiceIntentInfo>() { + @Override + public ParsedServiceIntentInfo createFromParcel(Parcel source) { + return new ParsedServiceIntentInfo(source); + } + + @Override + public ParsedServiceIntentInfo[] newArray(int size) { + return new ParsedServiceIntentInfo[size]; + } + }; + } + + public static class ParsedProviderIntentInfo extends ParsedIntentInfo { + + public ParsedProviderIntentInfo(String packageName, String className) { + super(packageName, className); + } + + public ParsedProviderIntentInfo(Parcel in) { + super(in); + } + + public static final Creator<ParsedProviderIntentInfo> CREATOR = + new Creator<ParsedProviderIntentInfo>() { + @Override + public ParsedProviderIntentInfo createFromParcel(Parcel source) { + return new ParsedProviderIntentInfo(source); + } + + @Override + public ParsedProviderIntentInfo[] newArray(int size) { + return new ParsedProviderIntentInfo[size]; + } + }; + } + + public static class ParsedQueriesIntentInfo extends ParsedIntentInfo { + + public ParsedQueriesIntentInfo(String packageName, String className) { + super(packageName, className); + } + + public ParsedQueriesIntentInfo(Parcel in) { + super(in); + } + + public static final Creator<ParsedQueriesIntentInfo> CREATOR = + new Creator<ParsedQueriesIntentInfo>() { + @Override + public ParsedQueriesIntentInfo createFromParcel(Parcel source) { + return new ParsedQueriesIntentInfo(source); + } + + @Override + public ParsedQueriesIntentInfo[] newArray(int size) { + return new ParsedQueriesIntentInfo[size]; + } + }; + } + + public static class ParsedComponent<IntentInfoType extends ParsedIntentInfo> implements + Parcelable { + + // TODO(b/135203078): Replace with "name", as not all usages are an actual class + public String className; + public int icon; + public int labelRes; + public CharSequence nonLocalizedLabel; + public int logo; + public int banner; + + public int descriptionRes; + + // TODO(b/135203078): Make subclass that contains these fields only for the necessary + // subtypes + protected boolean enabled = true; + protected boolean directBootAware; + public int flags; + + private String packageName; + private String splitName; + + // TODO(b/135203078): Make nullable + public List<IntentInfoType> intents = new ArrayList<>(); + + private transient ComponentName componentName; + + protected Bundle metaData; + + public void setSplitName(String splitName) { + this.splitName = splitName; + } + + public String getSplitName() { + return splitName; + } + + @CallSuper + public void setPackageName(String packageName) { + this.packageName = packageName; + this.componentName = null; + } + + void setPackageNameInternal(String packageName) { + this.packageName = packageName; + this.componentName = null; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getPackageName() { + return packageName; + } + + public final boolean isDirectBootAware() { + return directBootAware; + } + + public final boolean isEnabled() { + return enabled; + } + + public final String getName() { + return className; + } + + public final Bundle getMetaData() { + return metaData; + } + + @UnsupportedAppUsage + public ComponentName getComponentName() { + if (componentName != null) { + return componentName; + } + if (className != null) { + componentName = new ComponentName(getPackageName(), + className); + } + return componentName; + } + + public void setFrom(ParsedComponent other) { + this.metaData = other.metaData; + this.className = other.className; + this.icon = other.icon; + this.labelRes = other.labelRes; + this.nonLocalizedLabel = other.nonLocalizedLabel; + this.logo = other.logo; + this.banner = other.banner; + + this.descriptionRes = other.descriptionRes; + + this.enabled = other.enabled; + this.directBootAware = other.directBootAware; + this.flags = other.flags; + + this.setPackageName(other.packageName); + this.setSplitName(other.getSplitName()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.className); + dest.writeInt(this.icon); + dest.writeInt(this.labelRes); + dest.writeCharSequence(this.nonLocalizedLabel); + dest.writeInt(this.logo); + dest.writeInt(this.banner); + dest.writeInt(this.descriptionRes); + dest.writeBoolean(this.enabled); + dest.writeBoolean(this.directBootAware); + dest.writeInt(this.flags); + dest.writeString(this.packageName); + dest.writeString(this.splitName); + ParsedIntentInfo.writeIntentsList(this.intents, dest, flags); + dest.writeBundle(this.metaData); + } + + public ParsedComponent() { + } + + protected ParsedComponent(Parcel in) { + // We use the boot classloader for all classes that we load. + final ClassLoader boot = Object.class.getClassLoader(); + this.className = in.readString(); + this.icon = in.readInt(); + this.labelRes = in.readInt(); + this.nonLocalizedLabel = in.readCharSequence(); + this.logo = in.readInt(); + this.banner = in.readInt(); + this.descriptionRes = in.readInt(); + this.enabled = in.readByte() != 0; + this.directBootAware = in.readByte() != 0; + this.flags = in.readInt(); + this.packageName = in.readString(); + this.splitName = in.readString(); + this.intents = ParsedIntentInfo.createIntentsList(in); + this.metaData = in.readBundle(boot); + } + } + + // TODO(b/135203078): Document this. Maybe split out ParsedComponent to be actual components + // that can have their own processes, rather than something like permission which cannot. + public static class ParsedMainComponent<IntentInfoType extends ParsedIntentInfo> extends + ParsedComponent<IntentInfoType> { + + private String processName; + private String permission; + + public void setProcessName(String appProcessName, String processName) { + // TODO(b/135203078): Is this even necessary anymore? + this.processName = TextUtils.safeIntern( + processName == null ? appProcessName : processName); + } + + public String getProcessName() { + return processName; + } + + public void setPermission(String permission) { + this.permission = TextUtils.safeIntern(permission); + } + + public String getPermission() { + return permission; + } + + @Override + public void setFrom(ParsedComponent other) { + super.setFrom(other); + if (other instanceof ParsedMainComponent) { + ParsedMainComponent otherMainComponent = (ParsedMainComponent) other; + this.setProcessName(otherMainComponent.getProcessName(), + otherMainComponent.getProcessName()); + this.setPermission(otherMainComponent.getPermission()); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(this.processName); + dest.writeString(this.permission); + } + + public ParsedMainComponent() { + } + + protected ParsedMainComponent(Parcel in) { + super(in); + this.processName = TextUtils.safeIntern(in.readString()); + this.permission = TextUtils.safeIntern(in.readString()); + } + + public static final Creator<ParsedMainComponent> CREATOR = + new Creator<ParsedMainComponent>() { + @Override + public ParsedMainComponent createFromParcel(Parcel source) { + return new ParsedMainComponent(source); + } + + @Override + public ParsedMainComponent[] newArray(int size) { + return new ParsedMainComponent[size]; + } + }; + } + + public static class ParsedActivity extends ParsedMainComponent<ParsedActivityIntentInfo> + implements Parcelable { + + public boolean exported; + public int theme; + public int uiOptions; + + public String targetActivity; + + public String parentActivityName; + public String taskAffinity; + public int privateFlags; + + public int launchMode; + public int documentLaunchMode; + public int maxRecents; + public int configChanges; + public int softInputMode; + public int persistableMode; + public int lockTaskLaunchMode; + + public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + public int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + + public float maxAspectRatio; + public boolean hasMaxAspectRatio; + + public float minAspectRatio; + public boolean hasMinAspectRatio; + + public String requestedVrComponent; + public int rotationAnimation = -1; + public int colorMode; + public int order; + + public ActivityInfo.WindowLayout windowLayout; + + @Override + public void setPackageName(String packageName) { + super.setPackageName(packageName); + for (ParsedIntentInfo intent : this.intents) { + intent.packageName = packageName; + } + } + + public boolean hasMaxAspectRatio() { + return hasMaxAspectRatio; + } + + public boolean hasMinAspectRatio() { + return hasMinAspectRatio; + } + + public void setMaxAspectRatio(int resizeMode, float maxAspectRatio) { + if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE + || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) { + // Resizeable activities can be put in any aspect ratio. + return; + } + + if (maxAspectRatio < 1.0f && maxAspectRatio != 0) { + // Ignore any value lesser than 1.0. + return; + } + + this.maxAspectRatio = maxAspectRatio; + hasMaxAspectRatio = true; + } + + public void setMinAspectRatio(int resizeMode, float minAspectRatio) { + if (resizeMode == RESIZE_MODE_RESIZEABLE + || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) { + // Resizeable activities can be put in any aspect ratio. + return; + } + + if (minAspectRatio < 1.0f && minAspectRatio != 0) { + // Ignore any value lesser than 1.0. + return; + } + + this.minAspectRatio = minAspectRatio; + hasMinAspectRatio = true; + } + + public void addIntent(ParsedActivityIntentInfo intent) { + this.intents.add(intent); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeBoolean(this.exported); + dest.writeInt(this.theme); + dest.writeInt(this.uiOptions); + dest.writeString(this.targetActivity); + dest.writeString(this.parentActivityName); + dest.writeString(this.taskAffinity); + dest.writeInt(this.privateFlags); + dest.writeInt(this.launchMode); + dest.writeInt(this.documentLaunchMode); + dest.writeInt(this.maxRecents); + dest.writeInt(this.configChanges); + dest.writeInt(this.softInputMode); + dest.writeInt(this.persistableMode); + dest.writeInt(this.lockTaskLaunchMode); + dest.writeInt(this.screenOrientation); + dest.writeInt(this.resizeMode); + dest.writeFloat(this.maxAspectRatio); + dest.writeBoolean(this.hasMaxAspectRatio); + dest.writeFloat(this.minAspectRatio); + dest.writeBoolean(this.hasMinAspectRatio); + dest.writeString(this.requestedVrComponent); + dest.writeInt(this.rotationAnimation); + dest.writeInt(this.colorMode); + dest.writeInt(this.order); + dest.writeBundle(this.metaData); + + if (windowLayout != null) { + dest.writeInt(1); + dest.writeInt(windowLayout.width); + dest.writeFloat(windowLayout.widthFraction); + dest.writeInt(windowLayout.height); + dest.writeFloat(windowLayout.heightFraction); + dest.writeInt(windowLayout.gravity); + dest.writeInt(windowLayout.minWidth); + dest.writeInt(windowLayout.minHeight); + } else { + dest.writeInt(0); + } + } + + public ParsedActivity() { + } + + protected ParsedActivity(Parcel in) { + super(in); + this.exported = in.readByte() != 0; + this.theme = in.readInt(); + this.uiOptions = in.readInt(); + this.targetActivity = in.readString(); + this.parentActivityName = in.readString(); + this.taskAffinity = in.readString(); + this.privateFlags = in.readInt(); + this.launchMode = in.readInt(); + this.documentLaunchMode = in.readInt(); + this.maxRecents = in.readInt(); + this.configChanges = in.readInt(); + this.softInputMode = in.readInt(); + this.persistableMode = in.readInt(); + this.lockTaskLaunchMode = in.readInt(); + this.screenOrientation = in.readInt(); + this.resizeMode = in.readInt(); + this.maxAspectRatio = in.readFloat(); + this.hasMaxAspectRatio = in.readByte() != 0; + this.minAspectRatio = in.readFloat(); + this.hasMinAspectRatio = in.readByte() != 0; + this.requestedVrComponent = in.readString(); + this.rotationAnimation = in.readInt(); + this.colorMode = in.readInt(); + this.order = in.readInt(); + this.metaData = in.readBundle(); + if (in.readInt() == 1) { + windowLayout = new ActivityInfo.WindowLayout(in); + } + } + + public static final Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() { + @Override + public ParsedActivity createFromParcel(Parcel source) { + return new ParsedActivity(source); + } + + @Override + public ParsedActivity[] newArray(int size) { + return new ParsedActivity[size]; + } + }; + } + + public static class ParsedService extends ParsedMainComponent<ParsedServiceIntentInfo> { + + public boolean exported; + public int flags; + public int foregroundServiceType; + public int order; + + @Override + public void setPackageName(String packageName) { + super.setPackageName(packageName); + for (ParsedIntentInfo intent : this.intents) { + intent.packageName = packageName; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeBoolean(this.exported); + dest.writeBundle(this.metaData); + dest.writeInt(this.flags); + dest.writeInt(this.foregroundServiceType); + dest.writeInt(this.order); + } + + public ParsedService() { + } + + protected ParsedService(Parcel in) { + super(in); + this.exported = in.readByte() != 0; + this.metaData = in.readBundle(); + this.flags = in.readInt(); + this.foregroundServiceType = in.readInt(); + this.order = in.readInt(); + } + + public static final Creator<ParsedService> CREATOR = new Creator<ParsedService>() { + @Override + public ParsedService createFromParcel(Parcel source) { + return new ParsedService(source); + } + + @Override + public ParsedService[] newArray(int size) { + return new ParsedService[size]; + } + }; + } + + public static class ParsedProvider extends ParsedMainComponent<ParsedProviderIntentInfo> { + + protected boolean exported; + protected int flags; + protected int order; + private String authority; + protected boolean isSyncable; + private String readPermission; + private String writePermission; + protected boolean grantUriPermissions; + protected boolean forceUriPermissions; + protected boolean multiProcess; + protected int initOrder; + protected PatternMatcher[] uriPermissionPatterns; + protected PathPermission[] pathPermissions; + + protected void setFrom(ParsedProvider other) { + super.setFrom(other); + this.exported = other.exported; + + this.intents.clear(); + if (other.intents != null) { + this.intents.addAll(other.intents); + } + + this.flags = other.flags; + this.order = other.order; + this.setAuthority(other.getAuthority()); + this.isSyncable = other.isSyncable; + this.setReadPermission(other.getReadPermission()); + this.setWritePermission(other.getWritePermission()); + this.grantUriPermissions = other.grantUriPermissions; + this.forceUriPermissions = other.forceUriPermissions; + this.multiProcess = other.multiProcess; + this.initOrder = other.initOrder; + this.uriPermissionPatterns = other.uriPermissionPatterns; + this.pathPermissions = other.pathPermissions; + } + + @Override + public void setPackageName(String packageName) { + super.setPackageName(packageName); + for (ParsedIntentInfo intent : this.intents) { + intent.packageName = packageName; + } + } + + public boolean isExported() { + return exported; + } + + public List<ParsedProviderIntentInfo> getIntents() { + return intents; + } + + public int getFlags() { + return flags; + } + + public int getOrder() { + return order; + } + + public void setAuthority(String authority) { + this.authority = TextUtils.safeIntern(authority); + } + + public String getAuthority() { + return authority; + } + + public boolean isSyncable() { + return isSyncable; + } + + public void setReadPermission(String readPermission) { + this.readPermission = TextUtils.safeIntern(readPermission); + } + + public String getReadPermission() { + return readPermission; + } + + public void setWritePermission(String writePermission) { + this.writePermission = TextUtils.safeIntern(writePermission); + } + + public String getWritePermission() { + return writePermission; + } + + public boolean isGrantUriPermissions() { + return grantUriPermissions; + } + + public boolean isForceUriPermissions() { + return forceUriPermissions; + } + + public boolean isMultiProcess() { + return multiProcess; + } + + public int getInitOrder() { + return initOrder; + } + + public PatternMatcher[] getUriPermissionPatterns() { + return uriPermissionPatterns; + } + + public PathPermission[] getPathPermissions() { + return pathPermissions; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeBoolean(this.exported); + dest.writeInt(this.flags); + dest.writeInt(this.order); + dest.writeString(this.authority); + dest.writeBoolean(this.isSyncable); + dest.writeString(this.readPermission); + dest.writeString(this.writePermission); + dest.writeBoolean(this.grantUriPermissions); + dest.writeBoolean(this.forceUriPermissions); + dest.writeBoolean(this.multiProcess); + dest.writeInt(this.initOrder); + dest.writeTypedArray(this.uriPermissionPatterns, flags); + dest.writeTypedArray(this.pathPermissions, flags); + } + + public ParsedProvider() { + } + + protected ParsedProvider(Parcel in) { + super(in); + this.exported = in.readByte() != 0; + this.flags = in.readInt(); + this.order = in.readInt(); + this.authority = TextUtils.safeIntern(in.readString()); + this.isSyncable = in.readByte() != 0; + this.readPermission = TextUtils.safeIntern(in.readString()); + this.writePermission = TextUtils.safeIntern(in.readString()); + this.grantUriPermissions = in.readByte() != 0; + this.forceUriPermissions = in.readByte() != 0; + this.multiProcess = in.readByte() != 0; + this.initOrder = in.readInt(); + this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR); + this.pathPermissions = in.createTypedArray(PathPermission.CREATOR); + } + + public static final Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() { + @Override + public ParsedProvider createFromParcel(Parcel source) { + return new ParsedProvider(source); + } + + @Override + public ParsedProvider[] newArray(int size) { + return new ParsedProvider[size]; + } + }; + } + + public static class ParsedPermission extends ParsedComponent<ParsedIntentInfo> { + + public String backgroundPermission; + private String group; + public int requestRes; + public int protectionLevel; + public boolean tree; + + public ParsedPermissionGroup parsedPermissionGroup; + + public void setName(String className) { + this.className = className; + } + + public void setGroup(String group) { + this.group = TextUtils.safeIntern(group); + } + + public String getGroup() { + return group; + } + + public boolean isRuntime() { + return getProtection() == PermissionInfo.PROTECTION_DANGEROUS; + } + + public boolean isAppOp() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; + } + + @PermissionInfo.Protection + public int getProtection() { + return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + } + + public int getProtectionFlags() { + return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE; + } + + public int calculateFootprint() { + int size = getName().length(); + if (nonLocalizedLabel != null) { + size += nonLocalizedLabel.length(); + } + return size; + } + + public ParsedPermission() { + } + + public ParsedPermission(ParsedPermission other) { + // TODO(b/135203078): Better way to copy this? Maybe refactor to the point where copy + // isn't needed. + this.className = other.className; + this.icon = other.icon; + this.labelRes = other.labelRes; + this.nonLocalizedLabel = other.nonLocalizedLabel; + this.logo = other.logo; + this.banner = other.banner; + this.descriptionRes = other.descriptionRes; + this.enabled = other.enabled; + this.directBootAware = other.directBootAware; + this.flags = other.flags; + this.setSplitName(other.getSplitName()); + this.setPackageName(other.getPackageName()); + + this.intents.addAll(other.intents); + + if (other.metaData != null) { + this.metaData = new Bundle(); + this.metaData.putAll(other.metaData); + } + + this.backgroundPermission = other.backgroundPermission; + this.setGroup(other.group); + this.requestRes = other.requestRes; + this.protectionLevel = other.protectionLevel; + this.tree = other.tree; + + this.parsedPermissionGroup = other.parsedPermissionGroup; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(this.backgroundPermission); + dest.writeString(this.group); + dest.writeInt(this.requestRes); + dest.writeInt(this.protectionLevel); + dest.writeBoolean(this.tree); + dest.writeParcelable(this.parsedPermissionGroup, flags); + } + + protected ParsedPermission(Parcel in) { + super(in); + // We use the boot classloader for all classes that we load. + final ClassLoader boot = Object.class.getClassLoader(); + this.backgroundPermission = in.readString(); + this.group = TextUtils.safeIntern(in.readString()); + this.requestRes = in.readInt(); + this.protectionLevel = in.readInt(); + this.tree = in.readBoolean(); + this.parsedPermissionGroup = in.readParcelable(boot); + } + + public static final Creator<ParsedPermission> CREATOR = new Creator<ParsedPermission>() { + @Override + public ParsedPermission createFromParcel(Parcel source) { + return new ParsedPermission(source); + } + + @Override + public ParsedPermission[] newArray(int size) { + return new ParsedPermission[size]; + } + }; + } + + public static class ParsedPermissionGroup extends ParsedComponent<ParsedIntentInfo> { + + public int requestDetailResourceId; + public int backgroundRequestResourceId; + public int backgroundRequestDetailResourceId; + + public int requestRes; + public int priority; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(this.requestDetailResourceId); + dest.writeInt(this.backgroundRequestResourceId); + dest.writeInt(this.backgroundRequestDetailResourceId); + dest.writeInt(this.requestRes); + dest.writeInt(this.priority); + } + + public ParsedPermissionGroup() { + } + + protected ParsedPermissionGroup(Parcel in) { + super(in); + this.requestDetailResourceId = in.readInt(); + this.backgroundRequestResourceId = in.readInt(); + this.backgroundRequestDetailResourceId = in.readInt(); + this.requestRes = in.readInt(); + this.priority = in.readInt(); + } + + public static final Creator<ParsedPermissionGroup> CREATOR = + new Creator<ParsedPermissionGroup>() { + @Override + public ParsedPermissionGroup createFromParcel(Parcel source) { + return new ParsedPermissionGroup(source); + } + + @Override + public ParsedPermissionGroup[] newArray(int size) { + return new ParsedPermissionGroup[size]; + } + }; + } + + public static class ParsedInstrumentation extends ParsedComponent<ParsedIntentInfo> { + + private String targetPackage; + private String targetProcesses; + public boolean handleProfiling; + public boolean functionalTest; + + public ParsedInstrumentation() { + } + + public void setTargetPackage(String targetPackage) { + this.targetPackage = TextUtils.safeIntern(targetPackage); + } + + public String getTargetPackage() { + return targetPackage; + } + + public void setTargetProcesses(String targetProcesses) { + this.targetProcesses = TextUtils.safeIntern(targetProcesses); + } + + public String getTargetProcesses() { + return targetProcesses; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(this.targetPackage); + dest.writeString(this.targetProcesses); + dest.writeBoolean(this.handleProfiling); + dest.writeBoolean(this.functionalTest); + } + + protected ParsedInstrumentation(Parcel in) { + super(in); + this.targetPackage = TextUtils.safeIntern(in.readString()); + this.targetProcesses = TextUtils.safeIntern(in.readString()); + this.handleProfiling = in.readByte() != 0; + this.functionalTest = in.readByte() != 0; + } + + public static final Creator<ParsedInstrumentation> CREATOR = + new Creator<ParsedInstrumentation>() { + @Override + public ParsedInstrumentation createFromParcel(Parcel source) { + return new ParsedInstrumentation(source); + } + + @Override + public ParsedInstrumentation[] newArray(int size) { + return new ParsedInstrumentation[size]; + } + }; + } + + public static ParsedActivity parseActivity( + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, int flags, String[] outError, + boolean receiver, boolean hardwareAccelerated) + throws XmlPullParserException, IOException { + + TypedArray sa = null; + boolean visibleToEphemeral; + boolean setExported; + + int targetSdkVersion = parsingPackage.getTargetSdkVersion(); + String packageName = parsingPackage.getPackageName(); + String packageProcessName = parsingPackage.getProcessName(); + ParsedActivity result = new ParsedActivity(); + + try { + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity); + + String tag = receiver ? "<receiver>" : "<activity>"; + + String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_name, 0); + if (name == null) { + outError[0] = tag + " does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = tag + " invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestActivity_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivity_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivity_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivity_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivity_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + CharSequence pname; + if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) { + pname = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_process, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + pname = sa.getNonResourceString(R.styleable.AndroidManifestActivity_process); + } + + result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName, + packageProcessName, pname, + flags, separateProcesses, outError)); + + result.descriptionRes = sa.getResourceId( + R.styleable.AndroidManifestActivity_description, 0); + + result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivity_enabled, true); + + setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported); + if (setExported) { + result.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, + false); + } + + result.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0); + + result.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, + parsingPackage.getUiOptions()); + + String parentName = sa.getNonConfigurationString( + R.styleable.AndroidManifestActivity_parentActivityName, + Configuration.NATIVE_CONFIG_VERSION); + if (parentName != null) { + String parentClassName = ApkParseUtils.buildClassName(packageName, parentName); + if (parentClassName == null) { + Log.e(TAG, + "Activity " + result.className + + " specified invalid parentActivityName " + + parentName); + } else { + result.parentActivityName = parentClassName; + } + } + + String str; + str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0); + if (str == null) { + result.setPermission(parsingPackage.getPermission()); + } else { + result.setPermission(str); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestActivity_taskAffinity, + Configuration.NATIVE_CONFIG_VERSION); + result.taskAffinity = PackageParser.buildTaskAffinityName( + packageName, + parsingPackage.getTaskAffinity(), str, outError); + + result.setSplitName( + sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0)); + + result.flags = 0; + if (sa.getBoolean( + R.styleable.AndroidManifestActivity_multiprocess, false)) { + result.flags |= ActivityInfo.FLAG_MULTIPROCESS; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) { + result.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) { + result.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) { + result.flags |= ActivityInfo.FLAG_NO_HISTORY; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) { + result.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) { + result.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) { + result.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting, + (parsingPackage.getFlags() & ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) + != 0)) { + result.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, + false)) { + result.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false) + || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) { + result.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) { + result.flags |= ActivityInfo.FLAG_IMMERSIVE; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) { + result.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY; + } + + boolean directBootAware; + + if (!receiver) { + if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated, + hardwareAccelerated)) { + result.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED; + } + + result.launchMode = sa.getInt( + R.styleable.AndroidManifestActivity_launchMode, + ActivityInfo.LAUNCH_MULTIPLE); + result.documentLaunchMode = sa.getInt( + R.styleable.AndroidManifestActivity_documentLaunchMode, + ActivityInfo.DOCUMENT_LAUNCH_NONE); + result.maxRecents = sa.getInt( + R.styleable.AndroidManifestActivity_maxRecents, + ActivityTaskManager.getDefaultAppRecentsLimitStatic()); + result.configChanges = PackageParser.getActivityConfigChanges( + sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0), + sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0)); + result.softInputMode = sa.getInt( + R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); + + result.persistableMode = sa.getInteger( + R.styleable.AndroidManifestActivity_persistableMode, + ActivityInfo.PERSIST_ROOT_ONLY); + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) { + result.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, + false)) { + result.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, + false)) { + result.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) { + result.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING; + } + + int screenOrientation = sa.getInt( + R.styleable.AndroidManifestActivity_screenOrientation, + SCREEN_ORIENTATION_UNSPECIFIED); + result.screenOrientation = screenOrientation; + + int resizeMode = getActivityResizeMode(parsingPackage, sa, screenOrientation); + result.resizeMode = resizeMode; + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture, + false)) { + result.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) { + result.flags |= FLAG_ALWAYS_FOCUSABLE; + } + + if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio) + && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio) + == TypedValue.TYPE_FLOAT) { + result.setMaxAspectRatio(resizeMode, + sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio, + 0 /*default*/)); + } + + if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio) + && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio) + == TypedValue.TYPE_FLOAT) { + result.setMinAspectRatio(resizeMode, + sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio, + 0 /*default*/)); + } + + result.lockTaskLaunchMode = + sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0); + + directBootAware = sa.getBoolean( + R.styleable.AndroidManifestActivity_directBootAware, + false); + + result.requestedVrComponent = + sa.getString(R.styleable.AndroidManifestActivity_enableVrMode); + + result.rotationAnimation = + sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, + ROTATION_ANIMATION_UNSPECIFIED); + + result.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, + ActivityInfo.COLOR_MODE_DEFAULT); + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_showWhenLocked, false)) { + result.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_turnScreenOn, false)) { + result.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON; + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked, + false)) { + result.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED; + } + } else { + result.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + result.configChanges = 0; + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) { + result.flags |= ActivityInfo.FLAG_SINGLE_USER; + } + directBootAware = sa.getBoolean( + R.styleable.AndroidManifestActivity_directBootAware, + false); + } + + result.directBootAware = directBootAware; + + if (directBootAware) { + parsingPackage.setPartiallyDirectBootAware(true); + } + + // can't make this final; we may set it later via meta-data + visibleToEphemeral = sa.getBoolean( + R.styleable.AndroidManifestActivity_visibleToInstantApps, false); + if (visibleToEphemeral) { + result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; + parsingPackage.setVisibleToInstantApps(true); + } + } finally { + if (sa != null) { + sa.recycle(); + } + } + + + if (receiver && (parsingPackage.getPrivateFlags() + & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { + // A heavy-weight application can not have receives in its main process + if (result.getProcessName().equals(packageName)) { + outError[0] = "Heavy-weight applications can not have receivers in main process"; + return null; + } + } + + if (outError[0] != null) { + return null; + } + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getName().equals("intent-filter")) { + ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName, + result.className); + if (!parseIntentInfo(intentInfo, parsingPackage, res, parser, + true /*allowGlobs*/, + true /*allowAutoVerify*/, outError)) { + return null; + } + if (intentInfo.countActions() == 0) { + Slog.w(TAG, "No actions in intent filter at " + + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } else { + result.order = Math.max(intentInfo.getOrder(), result.order); + result.addIntent(intentInfo); + } + // adjust activity flags when we implicitly expose it via a browsable filter + final int visibility = visibleToEphemeral + ? IntentFilter.VISIBILITY_EXPLICIT + : !receiver && isImplicitlyExposedIntent(intentInfo) + ? IntentFilter.VISIBILITY_IMPLICIT + : IntentFilter.VISIBILITY_NONE; + intentInfo.setVisibilityToInstantApp(visibility); + if (intentInfo.isVisibleToInstantApp()) { + result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; + } + if (intentInfo.isImplicitlyVisibleToInstantApp()) { + result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; + } + if (PackageParser.LOG_UNSAFE_BROADCASTS && receiver + && (targetSdkVersion >= Build.VERSION_CODES.O)) { + for (int i = 0; i < intentInfo.countActions(); i++) { + final String action = intentInfo.getAction(i); + if (action == null || !action.startsWith("android.")) continue; + if (!PackageParser.SAFE_BROADCASTS.contains(action)) { + Slog.w(TAG, "Broadcast " + action + " may never be delivered to " + + packageName + " as requested at: " + + parser.getPositionDescription()); + } + } + } + } else if (!receiver && parser.getName().equals("preferred")) { + ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName, + result.className); + if (!parseIntentInfo(intentInfo, parsingPackage, res, parser, + false /*allowGlobs*/, + false /*allowAutoVerify*/, outError)) { + return null; + } + // adjust activity flags when we implicitly expose it via a browsable filter + final int visibility = visibleToEphemeral + ? IntentFilter.VISIBILITY_EXPLICIT + : !receiver && isImplicitlyExposedIntent(intentInfo) + ? IntentFilter.VISIBILITY_IMPLICIT + : IntentFilter.VISIBILITY_NONE; + intentInfo.setVisibilityToInstantApp(visibility); + if (intentInfo.isVisibleToInstantApp()) { + result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; + } + if (intentInfo.isImplicitlyVisibleToInstantApp()) { + result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; + } + + if (intentInfo.countActions() == 0) { + Slog.w(TAG, "No actions in preferred at " + + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } else { + parsingPackage.addPreferredActivityFilter(intentInfo); + } + } else if (parser.getName().equals("meta-data")) { + if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser, + result.metaData, + outError)) == null) { + return null; + } + } else if (!receiver && parser.getName().equals("layout")) { + result.windowLayout = parseLayout(res, parser); + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Problem in package " + parsingPackage.getBaseCodePath() + ":"); + if (receiver) { + Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName() + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } else { + Slog.w(TAG, "Unknown element under <activity>: " + parser.getName() + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + continue; + } else { + if (receiver) { + outError[0] = "Bad element under <receiver>: " + parser.getName(); + } else { + outError[0] = "Bad element under <activity>: " + parser.getName(); + } + return null; + } + } + } + + if (!setExported) { + result.exported = result.intents.size() > 0; + } + + return result; + } + + public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) { + return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE) + || intentInfo.hasAction(Intent.ACTION_SEND) + || intentInfo.hasAction(Intent.ACTION_SENDTO) + || intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE); + } + + public static int getActivityResizeMode( + ParsingPackage parsingPackage, + TypedArray sa, + int screenOrientation + ) { + int privateFlags = parsingPackage.getPrivateFlags(); + final boolean appExplicitDefault = (privateFlags + & (ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE + | ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0; + + if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity) + || appExplicitDefault) { + // Activity or app explicitly set if it is resizeable or not; + final boolean appResizeable = (privateFlags + & ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0; + if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, + appResizeable)) { + return ActivityInfo.RESIZE_MODE_RESIZEABLE; + } else { + return ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + } + } + + if ((privateFlags + & ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) + != 0) { + // The activity or app didn't explicitly set the resizing option, however we want to + // make it resize due to the sdk version it is targeting. + return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; + } + + // resize preference isn't set and target sdk version doesn't support resizing apps by + // default. For the app to be resizeable if it isn't fixed orientation or immersive. + if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) { + return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; + } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) { + return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; + } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { + return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; + } else { + return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; + } + } + + public static ParsedService parseService( + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, int flags, String[] outError + ) throws XmlPullParserException, IOException { + TypedArray sa = null; + boolean visibleToEphemeral; + boolean setExported; + + String packageName = parsingPackage.getPackageName(); + String packageProcessName = parsingPackage.getProcessName(); + ParsedService result = new ParsedService(); + + try { + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestService); + + String name = sa.getNonConfigurationString(R.styleable.AndroidManifestService_name, 0); + if (name == null) { + outError[0] = "<service> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<service> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestService_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestService_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestService_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestService_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestService_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + CharSequence pname; + if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) { + pname = sa.getNonConfigurationString(R.styleable.AndroidManifestService_process, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + pname = sa.getNonResourceString(R.styleable.AndroidManifestService_process); + } + + result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName, + packageProcessName, pname, + flags, separateProcesses, outError)); + + result.descriptionRes = sa.getResourceId( + R.styleable.AndroidManifestService_description, 0); + + result.enabled = sa.getBoolean(R.styleable.AndroidManifestService_enabled, true); + + setExported = sa.hasValue( + R.styleable.AndroidManifestService_exported); + if (setExported) { + result.exported = sa.getBoolean( + R.styleable.AndroidManifestService_exported, false); + } + + String str = sa.getNonConfigurationString( + R.styleable.AndroidManifestService_permission, 0); + if (str == null) { + result.setPermission(parsingPackage.getPermission()); + } else { + result.setPermission(str); + } + + result.setSplitName( + sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0)); + + result.foregroundServiceType = sa.getInt( + R.styleable.AndroidManifestService_foregroundServiceType, + ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); + + result.flags = 0; + if (sa.getBoolean( + R.styleable.AndroidManifestService_stopWithTask, + false)) { + result.flags |= ServiceInfo.FLAG_STOP_WITH_TASK; + } + if (sa.getBoolean( + R.styleable.AndroidManifestService_isolatedProcess, + false)) { + result.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS; + } + if (sa.getBoolean( + R.styleable.AndroidManifestService_externalService, + false)) { + result.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE; + } + if (sa.getBoolean( + R.styleable.AndroidManifestService_useAppZygote, + false)) { + result.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE; + } + if (sa.getBoolean( + R.styleable.AndroidManifestService_singleUser, + false)) { + result.flags |= ServiceInfo.FLAG_SINGLE_USER; + } + + result.directBootAware = sa.getBoolean( + R.styleable.AndroidManifestService_directBootAware, + false); + if (result.directBootAware) { + parsingPackage.setPartiallyDirectBootAware(true); + } + + visibleToEphemeral = sa.getBoolean( + R.styleable.AndroidManifestService_visibleToInstantApps, false); + if (visibleToEphemeral) { + result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP; + parsingPackage.setVisibleToInstantApps(true); + } + } finally { + if (sa != null) { + sa.recycle(); + } + } + + if (parsingPackage.cantSaveState()) { + // A heavy-weight application can not have services in its main process + // We can do direct compare because we intern all strings. + if (Objects.equals(result.getProcessName(), parsingPackage.getPackageName())) { + outError[0] = "Heavy-weight applications can not have services in main process"; + return null; + } + } + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getName().equals("intent-filter")) { + ParsedServiceIntentInfo intent = new ParsedServiceIntentInfo(packageName, + result.className); + if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/, + false /*allowAutoVerify*/, + outError)) { + return null; + } + if (visibleToEphemeral) { + intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); + result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP; + } + result.order = Math.max(intent.getOrder(), result.order); + result.intents.add(intent); + } else if (parser.getName().equals("meta-data")) { + if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser, + result.metaData, + outError)) == null) { + return null; + } + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <service>: " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + outError[0] = "Bad element under <service>: " + parser.getName(); + return null; + } + } + } + + if (!setExported) { + result.exported = result.intents.size() > 0; + } + + return result; + } + + public static ParsedProvider parseProvider( + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, int flags, String[] outError) + throws XmlPullParserException, IOException { + TypedArray sa = null; + String cpname; + boolean visibleToEphemeral; + + int targetSdkVersion = parsingPackage.getTargetSdkVersion(); + String packageName = parsingPackage.getPackageName(); + String packageProcessName = parsingPackage.getProcessName(); + ParsedProvider result = new ParsedProvider(); + + try { + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestProvider); + + String name = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_name, 0); + if (name == null) { + outError[0] = "<provider> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<provider> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestProvider_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestProvider_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestProvider_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestProvider_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestProvider_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + CharSequence pname; + if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) { + pname = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_process, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + pname = sa.getNonResourceString(R.styleable.AndroidManifestProvider_process); + } + + result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName, + packageProcessName, pname, + flags, separateProcesses, outError)); + + result.descriptionRes = sa.getResourceId( + R.styleable.AndroidManifestProvider_description, 0); + + result.enabled = sa.getBoolean(R.styleable.AndroidManifestProvider_enabled, true); + + boolean providerExportedDefault = false; + + if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) { + // For compatibility, applications targeting API level 16 or lower + // should have their content providers exported by default, unless they + // specify otherwise. + providerExportedDefault = true; + } + + result.exported = sa.getBoolean( + R.styleable.AndroidManifestProvider_exported, + providerExportedDefault); + + cpname = sa.getNonConfigurationString( + R.styleable.AndroidManifestProvider_authorities, 0); + + result.isSyncable = sa.getBoolean( + R.styleable.AndroidManifestProvider_syncable, + false); + + String permission = sa.getNonConfigurationString( + R.styleable.AndroidManifestProvider_permission, 0); + String str = sa.getNonConfigurationString( + R.styleable.AndroidManifestProvider_readPermission, 0); + if (str == null) { + str = permission; + } + if (str == null) { + result.setReadPermission(parsingPackage.getPermission()); + } else { + result.setReadPermission(str); + } + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestProvider_writePermission, 0); + if (str == null) { + str = permission; + } + if (str == null) { + result.setWritePermission(parsingPackage.getPermission()); + } else { + result.setWritePermission(str); + } + + result.grantUriPermissions = sa.getBoolean( + R.styleable.AndroidManifestProvider_grantUriPermissions, + false); + + result.forceUriPermissions = sa.getBoolean( + R.styleable.AndroidManifestProvider_forceUriPermissions, + false); + + result.multiProcess = sa.getBoolean( + R.styleable.AndroidManifestProvider_multiprocess, + false); + + result.initOrder = sa.getInt( + R.styleable.AndroidManifestProvider_initOrder, + 0); + + result.setSplitName( + sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0)); + + result.flags = 0; + + if (sa.getBoolean( + R.styleable.AndroidManifestProvider_singleUser, + false)) { + result.flags |= ProviderInfo.FLAG_SINGLE_USER; + } + + result.directBootAware = sa.getBoolean( + R.styleable.AndroidManifestProvider_directBootAware, + false); + if (result.directBootAware) { + parsingPackage.setPartiallyDirectBootAware(true); + } + + visibleToEphemeral = + sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false); + if (visibleToEphemeral) { + result.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; + parsingPackage.setVisibleToInstantApps(true); + } + } finally { + if (sa != null) { + sa.recycle(); + } + } + + if ((parsingPackage.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) + != 0) { + // A heavy-weight application can not have providers in its main process + if (result.getProcessName().equals(packageName)) { + outError[0] = "Heavy-weight applications can not have providers in main process"; + return null; + } + } + + if (cpname == null) { + outError[0] = "<provider> does not include authorities attribute"; + return null; + } + if (cpname.length() <= 0) { + outError[0] = "<provider> has empty authorities attribute"; + return null; + } + result.setAuthority(cpname); + + if (!parseProviderTags(parsingPackage, res, parser, visibleToEphemeral, result, outError)) { + return null; + } + + return result; + } + + public static ParsedQueriesIntentInfo parsedParsedQueriesIntentInfo( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + ParsedQueriesIntentInfo intentInfo = new ParsedQueriesIntentInfo( + parsingPackage.getPackageName(), + null + ); + if (!parseIntentInfo( + intentInfo, + parsingPackage, + res, + parser, + true /*allowGlobs*/, + true /*allowAutoVerify*/, + outError + )) { + return null; + } + return intentInfo; + } + + private static boolean parseProviderTags( + ParsingPackage parsingPackage, + Resources res, XmlResourceParser parser, + boolean visibleToEphemeral, ParsedProvider outInfo, String[] outError) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getName().equals("intent-filter")) { + ParsedProviderIntentInfo intent = new ParsedProviderIntentInfo( + parsingPackage.getPackageName(), outInfo.className); + if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/, + false /*allowAutoVerify*/, + outError)) { + return false; + } + if (visibleToEphemeral) { + intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); + outInfo.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; + } + outInfo.order = Math.max(intent.getOrder(), outInfo.order); + outInfo.intents.add(intent); + + } else if (parser.getName().equals("meta-data")) { + Bundle metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser, + outInfo.metaData, outError); + if (metaData == null) { + return false; + } else { + outInfo.metaData = metaData; + } + + } else if (parser.getName().equals("grant-uri-permission")) { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestGrantUriPermission); + + PatternMatcher pa = null; + + String str = sa.getNonConfigurationString( + R.styleable.AndroidManifestGrantUriPermission_path, 0); + if (str != null) { + pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0); + if (str != null) { + pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0); + if (str != null) { + pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB); + } + + sa.recycle(); + + if (pa != null) { + if (outInfo.uriPermissionPatterns == null) { + outInfo.uriPermissionPatterns = new PatternMatcher[1]; + outInfo.uriPermissionPatterns[0] = pa; + } else { + final int N = outInfo.uriPermissionPatterns.length; + PatternMatcher[] newp = new PatternMatcher[N + 1]; + System.arraycopy(outInfo.uriPermissionPatterns, 0, newp, 0, N); + newp[N] = pa; + outInfo.uriPermissionPatterns = newp; + } + outInfo.grantUriPermissions = true; + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <path-permission>: " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>"; + return false; + } + } + XmlUtils.skipCurrentTag(parser); + + } else if (parser.getName().equals("path-permission")) { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestPathPermission); + + PathPermission pa = null; + + String permission = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_permission, 0); + String readPermission = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_readPermission, 0); + if (readPermission == null) { + readPermission = permission; + } + String writePermission = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_writePermission, 0); + if (writePermission == null) { + writePermission = permission; + } + + boolean havePerm = false; + if (readPermission != null) { + readPermission = readPermission.intern(); + havePerm = true; + } + if (writePermission != null) { + writePermission = writePermission.intern(); + havePerm = true; + } + + if (!havePerm) { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "No readPermission or writePermssion for <path-permission>: " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + outError[0] = "No readPermission or writePermssion for <path-permission>"; + return false; + } + } + + String path = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_path, 0); + if (path != null) { + pa = new PathPermission(path, + PatternMatcher.PATTERN_LITERAL, readPermission, writePermission); + } + + path = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_pathPrefix, 0); + if (path != null) { + pa = new PathPermission(path, + PatternMatcher.PATTERN_PREFIX, readPermission, writePermission); + } + + path = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_pathPattern, 0); + if (path != null) { + pa = new PathPermission(path, + PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission); + } + + path = sa.getNonConfigurationString( + R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0); + if (path != null) { + pa = new PathPermission(path, + PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission); + } + + sa.recycle(); + + if (pa != null) { + if (outInfo.pathPermissions == null) { + outInfo.pathPermissions = new PathPermission[1]; + outInfo.pathPermissions[0] = pa; + } else { + final int N = outInfo.pathPermissions.length; + PathPermission[] newp = new PathPermission[N + 1]; + System.arraycopy(outInfo.pathPermissions, 0, newp, 0, N); + newp[N] = pa; + outInfo.pathPermissions = newp; + } + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>"; + return false; + } + XmlUtils.skipCurrentTag(parser); + + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <provider>: " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + outError[0] = "Bad element under <provider>: " + parser.getName(); + return false; + } + } + } + return true; + } + + public static ParsedActivity parseActivityAlias( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + String[] outError) + throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestActivityAlias); + + String targetActivity = sa.getNonConfigurationString( + R.styleable.AndroidManifestActivityAlias_targetActivity, + Configuration.NATIVE_CONFIG_VERSION); + if (targetActivity == null) { + outError[0] = "<activity-alias> does not specify android:targetActivity"; + sa.recycle(); + return null; + } + + String packageName = parsingPackage.getPackageName(); + targetActivity = ApkParseUtils.buildClassName(packageName, targetActivity); + if (targetActivity == null) { + outError[0] = "Empty class name in package " + packageName; + sa.recycle(); + return null; + } + + ParsedActivity target = null; + + List<ParsedActivity> activities = parsingPackage.getActivities(); + final int NA = activities.size(); + for (int i = 0; i < NA; i++) { + ParsedActivity t = activities.get(i); + if (targetActivity.equals(t.className)) { + target = t; + break; + } + } + + if (target == null) { + outError[0] = "<activity-alias> target activity " + targetActivity + + " not found in manifest with activities = " + parsingPackage.getActivities() + + ", parsedActivities = " + activities; + sa.recycle(); + return null; + } + + ParsedActivity result = new ParsedActivity(); + result.setPackageNameInternal(target.getPackageName()); + result.targetActivity = targetActivity; + result.configChanges = target.configChanges; + result.flags = target.flags; + result.privateFlags = target.privateFlags; + result.icon = target.icon; + result.logo = target.logo; + result.banner = target.banner; + result.labelRes = target.labelRes; + result.nonLocalizedLabel = target.nonLocalizedLabel; + result.launchMode = target.launchMode; + result.lockTaskLaunchMode = target.lockTaskLaunchMode; + result.descriptionRes = target.descriptionRes; + result.screenOrientation = target.screenOrientation; + result.taskAffinity = target.taskAffinity; + result.theme = target.theme; + result.softInputMode = target.softInputMode; + result.uiOptions = target.uiOptions; + result.parentActivityName = target.parentActivityName; + result.maxRecents = target.maxRecents; + result.windowLayout = target.windowLayout; + result.resizeMode = target.resizeMode; + result.maxAspectRatio = target.maxAspectRatio; + result.hasMaxAspectRatio = target.hasMaxAspectRatio; + result.minAspectRatio = target.minAspectRatio; + result.hasMinAspectRatio = target.hasMinAspectRatio; + result.requestedVrComponent = target.requestedVrComponent; + result.directBootAware = target.directBootAware; + + result.setProcessName(parsingPackage.getAppInfoProcessName(), target.getProcessName()); + + // Not all attributes from the target ParsedActivity are copied to the alias. + // Careful when adding an attribute and determine whether or not it should be copied. +// result.enabled = target.enabled; +// result.exported = target.exported; +// result.permission = target.permission; +// result.splitName = target.splitName; +// result.documentLaunchMode = target.documentLaunchMode; +// result.persistableMode = target.persistableMode; +// result.rotationAnimation = target.rotationAnimation; +// result.colorMode = target.colorMode; +// result.intents.addAll(target.intents); +// result.order = target.order; +// result.metaData = target.metaData; + + String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivityAlias_name, + 0); + if (name == null) { + outError[0] = "<activity-alias> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<activity-alias> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestActivityAlias_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivityAlias_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + result.descriptionRes = sa.getResourceId( + R.styleable.AndroidManifestActivityAlias_description, 0); + + result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivityAlias_enabled, true); + + final boolean setExported = sa.hasValue( + R.styleable.AndroidManifestActivityAlias_exported); + if (setExported) { + result.exported = sa.getBoolean( + R.styleable.AndroidManifestActivityAlias_exported, false); + } + + String str; + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestActivityAlias_permission, 0); + if (str != null) { + result.setPermission(str); + } + + String parentName = sa.getNonConfigurationString( + R.styleable.AndroidManifestActivityAlias_parentActivityName, + Configuration.NATIVE_CONFIG_VERSION); + if (parentName != null) { + String parentClassName = ApkParseUtils.buildClassName(result.getPackageName(), + parentName); + if (parentClassName == null) { + Log.e(TAG, "Activity alias " + result.className + + " specified invalid parentActivityName " + parentName); + outError[0] = null; + } else { + result.parentActivityName = parentClassName; + } + } + + // TODO add visibleToInstantApps attribute to activity alias + final boolean visibleToEphemeral = + ((result.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0); + + sa.recycle(); + + if (outError[0] != null) { + return null; + } + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("intent-filter")) { + ParsedActivityIntentInfo intent = new ParsedActivityIntentInfo(packageName, + result.className); + if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/, + true /*allowAutoVerify*/, outError)) { + return null; + } + if (intent.countActions() == 0) { + Slog.w(TAG, "No actions in intent filter at " + + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + } else { + result.order = Math.max(intent.getOrder(), result.order); + result.addIntent(intent); + } + // adjust activity flags when we implicitly expose it via a browsable filter + final int visibility = visibleToEphemeral + ? IntentFilter.VISIBILITY_EXPLICIT + : isImplicitlyExposedIntent(intent) + ? IntentFilter.VISIBILITY_IMPLICIT + : IntentFilter.VISIBILITY_NONE; + intent.setVisibilityToInstantApp(visibility); + if (intent.isVisibleToInstantApp()) { + result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; + } + if (intent.isImplicitlyVisibleToInstantApp()) { + result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; + } + } else if (tagName.equals("meta-data")) { + if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser, + result.metaData, + outError)) == null) { + return null; + } + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <activity-alias>: " + tagName + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + outError[0] = "Bad element under <activity-alias>: " + tagName; + return null; + } + } + } + + if (!setExported) { + result.exported = result.intents.size() > 0; + } + + return result; + } + + public static ParsedPermission parsePermission( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = null; + String packageName = parsingPackage.getPackageName(); + ParsedPermission result = new ParsedPermission(); + + try { + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission); + + String name = sa.getNonConfigurationString(R.styleable.AndroidManifestPermission_name, + 0); + if (name == null) { + outError[0] = "<permission> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<permission> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestPermission_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermission_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermission_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermission_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermission_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + result.descriptionRes = sa.getResourceId( + R.styleable.AndroidManifestPermission_description, 0); + + if (sa.hasValue( + R.styleable.AndroidManifestPermission_backgroundPermission)) { + if ("android".equals(packageName)) { + result.backgroundPermission = sa.getNonResourceString( + R.styleable + .AndroidManifestPermission_backgroundPermission); + } else { + Slog.w(TAG, packageName + " defines a background permission. Only the " + + "'android' package can do that."); + } + } + + // Note: don't allow this value to be a reference to a resource + // that may change. + result.setGroup(sa.getNonResourceString( + R.styleable.AndroidManifestPermission_permissionGroup)); + + result.requestRes = sa.getResourceId( + R.styleable.AndroidManifestPermission_request, 0); + + result.protectionLevel = sa.getInt( + R.styleable.AndroidManifestPermission_protectionLevel, + PermissionInfo.PROTECTION_NORMAL); + + result.flags = sa.getInt( + R.styleable.AndroidManifestPermission_permissionFlags, 0); + + // For now only platform runtime permissions can be restricted + if (!result.isRuntime() || !"android".equals(result.getPackageName())) { + result.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED; + result.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED; + } else { + // The platform does not get to specify conflicting permissions + if ((result.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0 + && (result.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) { + throw new IllegalStateException("Permission cannot be both soft and hard" + + " restricted: " + result.getName()); + } + } + + } finally { + if (sa != null) { + sa.recycle(); + } + } + + if (result.protectionLevel == -1) { + outError[0] = "<permission> does not specify protectionLevel"; + return null; + } + + result.protectionLevel = PermissionInfo.fixProtectionLevel(result.protectionLevel); + + if (result.getProtectionFlags() != 0) { + if ((result.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0 + && (result.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) + == 0 + && (result.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) != + PermissionInfo.PROTECTION_SIGNATURE) { + outError[0] = "<permission> protectionLevel specifies a non-instant flag but is " + + "not based on signature type"; + return null; + } + } + + boolean success = parseAllMetaData(parsingPackage, res, parser, + "<permission>", result, outError); + if (!success || outError[0] != null) { + return null; + } + + return result; + } + + public static ParsedPermission parsePermissionTree( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = null; + String packageName = parsingPackage.getPackageName(); + ParsedPermission result = new ParsedPermission(); + + try { + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree); + + String name = sa.getNonConfigurationString( + R.styleable.AndroidManifestPermissionTree_name, 0); + if (name == null) { + outError[0] = "<permission-tree> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<permission-tree> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestPermissionTree_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionTree_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + } finally { + if (sa != null) { + sa.recycle(); + } + } + + int index = result.getName().indexOf('.'); + if (index > 0) { + index = result.getName().indexOf('.', index + 1); + } + if (index < 0) { + outError[0] = + "<permission-tree> name has less than three segments: " + result.getName(); + return null; + } + + result.descriptionRes = 0; + result.requestRes = 0; + result.protectionLevel = PermissionInfo.PROTECTION_NORMAL; + result.tree = true; + + boolean success = parseAllMetaData(parsingPackage, res, parser, + "<permission-tree>", result, outError); + if (!success || outError[0] != null) { + return null; + } + + return result; + } + + public static ParsedPermissionGroup parsePermissionGroup( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = null; + String packageName = parsingPackage.getPackageName(); + ParsedPermissionGroup result = new ParsedPermissionGroup(); + + try { + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup); + + String name = sa.getNonConfigurationString( + R.styleable.AndroidManifestPermissionGroup_name, 0); + if (name == null) { + outError[0] = "<permission> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<permission> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestPermissionGroup_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionGroup_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + result.descriptionRes = sa.getResourceId( + R.styleable.AndroidManifestPermissionGroup_description, 0); + + result.requestDetailResourceId = sa.getResourceId( + R.styleable.AndroidManifestPermissionGroup_requestDetail, 0); + result.backgroundRequestResourceId = sa.getResourceId( + R.styleable.AndroidManifestPermissionGroup_backgroundRequest, + 0); + result.backgroundRequestDetailResourceId = sa.getResourceId( + R.styleable + .AndroidManifestPermissionGroup_backgroundRequestDetail, 0); + + result.requestRes = sa.getResourceId( + R.styleable.AndroidManifestPermissionGroup_request, 0); + result.flags = sa.getInt( + R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, + 0); + result.priority = sa.getInt( + R.styleable.AndroidManifestPermissionGroup_priority, 0); + + } finally { + if (sa != null) { + sa.recycle(); + } + } + + boolean success = parseAllMetaData(parsingPackage, res, parser, + "<permission-group>", result, outError); + if (!success || outError[0] != null) { + return null; + } + + return result; + } + + public static ParsedInstrumentation parseInstrumentation( + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = null; + String packageName = parsingPackage.getPackageName(); + ParsedInstrumentation result = new ParsedInstrumentation(); + + try { + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation); + + // TODO(b/135203078): Re-share all of the configuration for this. ParseComponentArgs was + // un-used for this, but can be adjusted and re-added to share all the initial result + // parsing for icon/logo/name/etc in all of these parse methods. + String name = sa.getNonConfigurationString( + R.styleable.AndroidManifestInstrumentation_name, 0); + if (name == null) { + outError[0] = "<instrumentation> does not specify android:name"; + return null; + } else { + String className = ApkParseUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) { + outError[0] = "<instrumentation> invalid android:name"; + return null; + } else if (className == null) { + outError[0] = "Empty class name in package " + packageName; + return null; + } + + result.className = className; + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestInstrumentation_roundIcon, 0) : 0; + if (roundIconVal != 0) { + result.icon = roundIconVal; + result.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_icon, 0); + if (iconVal != 0) { + result.icon = iconVal; + result.nonLocalizedLabel = null; + } + } + + int logoVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_logo, 0); + if (logoVal != 0) { + result.logo = logoVal; + } + + int bannerVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_banner, 0); + if (bannerVal != 0) { + result.banner = bannerVal; + } + + TypedValue v = sa.peekValue(R.styleable.AndroidManifestInstrumentation_label); + if (v != null && (result.labelRes = v.resourceId) == 0) { + result.nonLocalizedLabel = v.coerceToString(); + } + + result.setPackageNameInternal(packageName); + + String str; + // Note: don't allow this value to be a reference to a resource + // that may change. + str = sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage); + result.setTargetPackage(str); + + str = sa.getNonResourceString( + R.styleable.AndroidManifestInstrumentation_targetProcesses); + result.setTargetProcesses(str); + result.handleProfiling = sa.getBoolean( + R.styleable.AndroidManifestInstrumentation_handleProfiling, false); + result.functionalTest = sa.getBoolean( + R.styleable.AndroidManifestInstrumentation_functionalTest, false); + + } finally { + if (sa != null) { + sa.recycle(); + } + } + + boolean success = parseAllMetaData(parsingPackage, res, parser, + "<instrumentation>", result, outError); + if (!success || outError[0] != null) { + return null; + } + + return result; + } + + public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) { + TypedArray sw = res.obtainAttributes(attrs, + R.styleable.AndroidManifestLayout); + int width = -1; + float widthFraction = -1f; + int height = -1; + float heightFraction = -1f; + final int widthType = sw.getType( + R.styleable.AndroidManifestLayout_defaultWidth); + if (widthType == TypedValue.TYPE_FRACTION) { + widthFraction = sw.getFraction( + R.styleable.AndroidManifestLayout_defaultWidth, + 1, 1, -1); + } else if (widthType == TypedValue.TYPE_DIMENSION) { + width = sw.getDimensionPixelSize( + R.styleable.AndroidManifestLayout_defaultWidth, + -1); + } + final int heightType = sw.getType( + R.styleable.AndroidManifestLayout_defaultHeight); + if (heightType == TypedValue.TYPE_FRACTION) { + heightFraction = sw.getFraction( + R.styleable.AndroidManifestLayout_defaultHeight, + 1, 1, -1); + } else if (heightType == TypedValue.TYPE_DIMENSION) { + height = sw.getDimensionPixelSize( + R.styleable.AndroidManifestLayout_defaultHeight, + -1); + } + int gravity = sw.getInt( + R.styleable.AndroidManifestLayout_gravity, + Gravity.CENTER); + int minWidth = sw.getDimensionPixelSize( + R.styleable.AndroidManifestLayout_minWidth, + -1); + int minHeight = sw.getDimensionPixelSize( + R.styleable.AndroidManifestLayout_minHeight, + -1); + sw.recycle(); + return new ActivityInfo.WindowLayout(width, widthFraction, + height, heightFraction, gravity, minWidth, minHeight); + } + + public static boolean parseIntentInfo( + ParsedIntentInfo intentInfo, + ParsingPackage parsingPackage, + Resources res, XmlResourceParser parser, boolean allowGlobs, + boolean allowAutoVerify, String[] outError + ) throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestIntentFilter); + + int priority = sa.getInt( + R.styleable.AndroidManifestIntentFilter_priority, 0); + intentInfo.setPriority(priority); + + int order = sa.getInt( + R.styleable.AndroidManifestIntentFilter_order, 0); + intentInfo.setOrder(order); + + TypedValue v = sa.peekValue( + R.styleable.AndroidManifestIntentFilter_label); + if (v != null && (intentInfo.labelRes = v.resourceId) == 0) { + intentInfo.nonLocalizedLabel = v.coerceToString(); + } + + int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId( + R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0; + if (roundIconVal != 0) { + intentInfo.icon = roundIconVal; + } else { + intentInfo.icon = sa.getResourceId( + R.styleable.AndroidManifestIntentFilter_icon, 0); + } + + if (allowAutoVerify) { + intentInfo.setAutoVerify(sa.getBoolean( + R.styleable.AndroidManifestIntentFilter_autoVerify, + false)); + } + + sa.recycle(); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String nodeName = parser.getName(); + if (nodeName.equals("action")) { + String value = parser.getAttributeValue( + PackageParser.ANDROID_RESOURCES, "name"); + if (TextUtils.isEmpty(value)) { + outError[0] = "No value supplied for <android:name>"; + return false; + } + XmlUtils.skipCurrentTag(parser); + + intentInfo.addAction(value); + } else if (nodeName.equals("category")) { + String value = parser.getAttributeValue( + PackageParser.ANDROID_RESOURCES, "name"); + if (TextUtils.isEmpty(value)) { + outError[0] = "No value supplied for <android:name>"; + return false; + } + XmlUtils.skipCurrentTag(parser); + + intentInfo.addCategory(value); + + } else if (nodeName.equals("data")) { + sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestData); + + String str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_mimeType, 0); + if (str != null) { + try { + intentInfo.addRawDataType(str); + } catch (IntentFilter.MalformedMimeTypeException e) { + outError[0] = e.toString(); + sa.recycle(); + return false; + } + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_scheme, 0); + if (str != null) { + intentInfo.addDataScheme(str); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_ssp, 0); + if (str != null) { + intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_sspPrefix, 0); + if (str != null) { + intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_sspPattern, 0); + if (str != null) { + if (!allowGlobs) { + outError[0] = "sspPattern not allowed here; ssp must be literal"; + return false; + } + intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB); + } + + String host = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_host, 0); + String port = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_port, 0); + if (host != null) { + intentInfo.addDataAuthority(host, port); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_path, 0); + if (str != null) { + intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathPrefix, 0); + if (str != null) { + intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathPattern, 0); + if (str != null) { + if (!allowGlobs) { + outError[0] = "pathPattern not allowed here; path must be literal"; + return false; + } + intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathAdvancedPattern, 0); + if (str != null) { + if (!allowGlobs) { + outError[0] = "pathAdvancedPattern not allowed here; path must be literal"; + return false; + } + intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB); + } + + sa.recycle(); + XmlUtils.skipCurrentTag(parser); + } else if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under <intent-filter>: " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + } else { + outError[0] = "Bad element under <intent-filter>: " + parser.getName(); + return false; + } + } + + intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT); + + if (PackageParser.DEBUG_PARSER) { + final StringBuilder cats = new StringBuilder("Intent d="); + cats.append(intentInfo.hasDefault); + cats.append(", cat="); + + final Iterator<String> it = intentInfo.categoriesIterator(); + if (it != null) { + while (it.hasNext()) { + cats.append(' '); + cats.append(it.next()); + } + } + Slog.d(TAG, cats.toString()); + } + + return true; + } + + private static boolean parseAllMetaData( + ParsingPackage parsingPackage, + Resources res, XmlResourceParser parser, String tag, + ParsedComponent outInfo, + String[] outError + ) throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getName().equals("meta-data")) { + if ((outInfo.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser, + outInfo.metaData, outError)) == null) { + return false; + } + } else { + if (!PackageParser.RIGID_PARSER) { + Slog.w(TAG, "Unknown element under " + tag + ": " + + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } else { + outError[0] = "Bad element under " + tag + ": " + parser.getName(); + } + } + } + + return true; + } + + public static boolean isImplicitlyExposedIntent(IntentFilter intent) { + return intent.hasCategory(Intent.CATEGORY_BROWSABLE) + || intent.hasAction(Intent.ACTION_SEND) + || intent.hasAction(Intent.ACTION_SENDTO) + || intent.hasAction(Intent.ACTION_SEND_MULTIPLE); + } +} diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java new file mode 100644 index 000000000000..377279e750c6 --- /dev/null +++ b/core/java/android/content/pm/parsing/PackageImpl.java @@ -0,0 +1,3234 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import static android.os.Build.VERSION_CODES.DONUT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; +import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; +import android.content.pm.parsing.ComponentParseUtils.ParsedProvider; +import android.content.pm.parsing.ComponentParseUtils.ParsedService; +import android.content.res.TypedArray; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Parcel; +import android.os.UserHandle; +import android.os.storage.StorageManager; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.SparseArray; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; +import com.android.server.SystemConfig; + +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * The backing data for a package that was parsed from disk. + * + * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case + * TODO(b/135203078): Field nullability annotations + * TODO(b/135203078): Convert = 1 fields into Booleans + * TODO(b/135203078): Make all lists nullable and Collections.unmodifiable immutable when returned. + * Prefer add/set methods if adding is necessary. + * TODO(b/135203078): Consider comments to disable auto-format and single-line, single-space all the + * get/set methods to make this class far more compact. Maybe even separate some logic into parent + * classes, assuming there is no overhead. + * TODO(b/135203078): Copy documentation from PackageParser#Package for the relevant fields included + * here. Should clarify and clean up any differences. Also consider renames if it helps make + * things clearer. + * TODO(b/135203078): Intern all possibl e String values? Initial refactor just mirrored old + * behavior. + * + * @hide + */ +public final class PackageImpl implements ParsingPackage, ParsedPackage, AndroidPackage, + AndroidPackageWrite { + + private static final String TAG = "PackageImpl"; + + // Resource boolean are -1, so 1 means we don't know the value. + private int supportsSmallScreens = 1; + private int supportsNormalScreens = 1; + private int supportsLargeScreens = 1; + private int supportsXLargeScreens = 1; + private int resizeable = 1; + private int anyDensity = 1; + + private long[] lastPackageUsageTimeInMills = + new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT]; + + private int versionCode; + private int versionCodeMajor; + private int baseRevisionCode; + private String versionName; + + private boolean coreApp; + private int compileSdkVersion; + private String compileSdkVersionCodename; + + private String packageName; + private String realPackage; + private String manifestPackageName; + private String baseCodePath; + + private boolean requiredForAllUsers; + private String restrictedAccountType; + private String requiredAccountType; + + private boolean baseHardwareAccelerated; + + private String overlayTarget; + private String overlayTargetName; + private String overlayCategory; + private int overlayPriority; + private boolean overlayIsStatic; + + private String staticSharedLibName; + private long staticSharedLibVersion; + private ArrayList<String> libraryNames; + private ArrayList<String> usesLibraries; + private ArrayList<String> usesOptionalLibraries; + + private ArrayList<String> usesStaticLibraries; + private long[] usesStaticLibrariesVersions; + private String[][] usesStaticLibrariesCertDigests; + + private String sharedUserId; + + private int sharedUserLabel; + private ArrayList<ConfigurationInfo> configPreferences; + private ArrayList<FeatureInfo> reqFeatures; + private ArrayList<FeatureGroupInfo> featureGroups; + + private byte[] restrictUpdateHash; + + private ArrayList<String> originalPackages; + private ArrayList<String> adoptPermissions; + + private ArrayList<String> requestedPermissions; + private ArrayList<String> implicitPermissions; + + private ArraySet<String> upgradeKeySets; + private Map<String, ArraySet<PublicKey>> keySetMapping; + + private ArrayList<String> protectedBroadcasts; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedActivity> activities; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedActivity> receivers; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedService> services; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedProvider> providers; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedPermission> permissions; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedPermissionGroup> permissionGroups; + + @Nullable + private ArrayList<ComponentParseUtils.ParsedInstrumentation> instrumentations; + + private ArrayList<ParsedActivityIntentInfo> preferredActivityFilters; + + private Bundle appMetaData; + + private String volumeUuid; + private String applicationVolumeUuid; + private PackageParser.SigningDetails signingDetails; + + private String codePath; + + private boolean use32BitAbi; + private boolean visibleToInstantApps; + + private String cpuAbiOverride; + + private boolean isStub; + + // TODO(b/135203078): Remove, should be unused + private int preferredOrder; + + private boolean forceQueryable; + + @Nullable + private ArrayList<Intent> queriesIntents; + + @Nullable + private ArrayList<String> queriesPackages; + + private String[] splitClassLoaderNames; + private String[] splitCodePaths; + private SparseArray<int[]> splitDependencies; + private int[] splitFlags; + private String[] splitNames; + private int[] splitRevisionCodes; + + // TODO(b/135203078): Audit applicationInfo.something usages, which may be different from + // package.something usages. There were differing cases of package.field = versus + // package.appInfo.field =. This class assumes some obvious ones, like packageName, + // were collapsible, but kept the following separate. + + private String applicationInfoBaseResourcePath; + private String applicationInfoCodePath; + private String applicationInfoResourcePath; + private String[] applicationInfoSplitResourcePaths; + + private String appComponentFactory; + private String backupAgentName; + private int banner; + private int category; + private String classLoaderName; + private String className; + private int compatibleWidthLimitDp; + private String credentialProtectedDataDir; + private String dataDir; + private int descriptionRes; + private String deviceProtectedDataDir; + private boolean enabled; + private int flags; + private int fullBackupContent; + private boolean hiddenUntilInstalled; + private int icon; + private int iconRes; + private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION; + private int labelRes; + private int largestWidthLimitDp; + private int logo; + private String manageSpaceActivityName; + private float maxAspectRatio; + private float minAspectRatio; + private int minSdkVersion; + private String name; + private String nativeLibraryDir; + private String nativeLibraryRootDir; + private boolean nativeLibraryRootRequiresIsa; + private int networkSecurityConfigRes; + private CharSequence nonLocalizedLabel; + private String permission; + private String primaryCpuAbi; + private int privateFlags; + private String processName; + private int requiresSmallestWidthDp; + private int roundIconRes; + private String secondaryCpuAbi; + private String secondaryNativeLibraryDir; + private String seInfo; + private String seInfoUser; + private int targetSandboxVersion; + private int targetSdkVersion; + private String taskAffinity; + private int theme; + private int uid = -1; + private int uiOptions; + private String[] usesLibraryFiles; + private List<SharedLibraryInfo> usesLibraryInfos; + private String zygotePreloadName; + + @VisibleForTesting + public PackageImpl( + String packageName, + String baseCodePath, + TypedArray manifestArray, + boolean isCoreApp + ) { + this.packageName = TextUtils.safeIntern(packageName); + this.manifestPackageName = this.packageName; + this.baseCodePath = baseCodePath; + + this.versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0); + this.versionCodeMajor = manifestArray.getInteger( + R.styleable.AndroidManifest_versionCodeMajor, 0); + this.baseRevisionCode = manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode, + 0); + setVersionName(manifestArray.getNonConfigurationString( + R.styleable.AndroidManifest_versionName, 0)); + this.coreApp = isCoreApp; + + this.compileSdkVersion = manifestArray.getInteger( + R.styleable.AndroidManifest_compileSdkVersion, 0); + setCompileSdkVersionCodename(manifestArray.getNonConfigurationString( + R.styleable.AndroidManifest_compileSdkVersionCodename, 0)); + } + + private PackageImpl(String packageName) { + this.packageName = TextUtils.safeIntern(packageName); + this.manifestPackageName = this.packageName; + } + + @VisibleForTesting + public static ParsingPackage forParsing(String packageName) { + return new PackageImpl(packageName); + } + + @VisibleForTesting + public static ParsingPackage forParsing( + String packageName, + String baseCodePath, + TypedArray manifestArray, + boolean isCoreApp) { + return new PackageImpl(packageName, baseCodePath, manifestArray, isCoreApp); + } + + /** + * Mock an unavailable {@link AndroidPackage} to use when removing a package from the system. + * This can occur if the package was installed on a storage device that has since been removed. + * Since the infrastructure uses {@link AndroidPackage}, but for this case only cares about + * volumeUuid, just fake it rather than having separate method paths. + */ + public static AndroidPackage buildFakeForDeletion(String packageName, String volumeUuid) { + return new PackageImpl(packageName) + .setVolumeUuid(volumeUuid) + .hideAsParsed() + .hideAsFinal(); + } + + @Override + public ParsedPackage hideAsParsed() { + return this; + } + + @Override + public AndroidPackage hideAsFinal() { + updateFlags(); + return this; + } + + @Override + @Deprecated + public AndroidPackageWrite mutate() { + return this; + } + + private void updateFlags() { + if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + this.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; + } + if (supportsNormalScreens != 0) { + this.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; + } + if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + this.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } + if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0 + && targetSdkVersion + >= Build.VERSION_CODES.GINGERBREAD)) { + this.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS; + } + if (resizeable < 0 || (resizeable > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + this.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS; + } + if (anyDensity < 0 || (anyDensity > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + this.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; + } + } + + @Override + public boolean usesCompatibilityMode() { + int flags = 0; + + if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; + } + if (supportsNormalScreens != 0) { + flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; + } + if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } + if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0 + && targetSdkVersion + >= Build.VERSION_CODES.GINGERBREAD)) { + flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS; + } + if (resizeable < 0 || (resizeable > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS; + } + if (anyDensity < 0 || (anyDensity > 0 + && targetSdkVersion + >= Build.VERSION_CODES.DONUT)) { + flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; + } + + return targetSdkVersion < DONUT + || (flags & (ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS + | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES + | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)) == 0; + } + + @Override + public String getBaseCodePath() { + return baseCodePath; + } + + @Override + public int getTargetSdkVersion() { + return targetSdkVersion; + } + + @Override + public String getPackageName() { + return packageName; + } + + @Override + public String getProcessName() { + return processName; + } + + @Override + public String getPermission() { + return permission; + } + + @Override + public String getStaticSharedLibName() { + return staticSharedLibName; + } + + @Override + public long getStaticSharedLibVersion() { + return staticSharedLibVersion; + } + + @Override + public String getSharedUserId() { + return sharedUserId; + } + + @Override + public List<String> getRequestedPermissions() { + return requestedPermissions == null ? Collections.emptyList() : requestedPermissions; + } + + @Nullable + @Override + public List<ParsedInstrumentation> getInstrumentations() { + return instrumentations; + } + + @Override + public Map<String, ArraySet<PublicKey>> getKeySetMapping() { + return keySetMapping == null ? Collections.emptyMap() : keySetMapping; + } + + @Override + public float getMaxAspectRatio() { + return maxAspectRatio; + } + + @Override + public float getMinAspectRatio() { + return minAspectRatio; + } + + @NonNull + @Override + public List<String> getLibraryNames() { + return libraryNames == null ? Collections.emptyList() : libraryNames; + } + + @Override + public List<ParsedActivity> getActivities() { + return activities == null ? Collections.emptyList() + : activities; + } + + @Override + public Bundle getAppMetaData() { + return appMetaData; + } + + @Nullable + @Override + public List<String> getUsesLibraries() { + return usesLibraries; + } + + @Nullable + @Override + public List<String> getUsesStaticLibraries() { + return usesStaticLibraries; + } + + @Override + public boolean isBaseHardwareAccelerated() { + return baseHardwareAccelerated; + } + + @Override + public int getUiOptions() { + return uiOptions; + } + + // TODO(b/135203078): Checking flags directly can be error prone, + // consider separate interface methods? + @Override + public int getFlags() { + return flags; + } + + // TODO(b/135203078): Checking flags directly can be error prone, + // consider separate interface methods? + @Override + public int getPrivateFlags() { + return privateFlags; + } + + @Override + public String getTaskAffinity() { + return taskAffinity; + } + + @Nullable + @Override + public List<String> getOriginalPackages() { + return originalPackages; + } + + @Override + public PackageParser.SigningDetails getSigningDetails() { + return signingDetails; + } + + @Override + public String getVolumeUuid() { + return volumeUuid; + } + + @Nullable + @Override + public List<ParsedPermissionGroup> getPermissionGroups() { + return permissionGroups; + } + + @Nullable + @Override + public List<ParsedPermission> getPermissions() { + return permissions; + } + + @Override + public String getCpuAbiOverride() { + return cpuAbiOverride; + } + + @Override + public String getPrimaryCpuAbi() { + return primaryCpuAbi; + } + + @Override + public String getSecondaryCpuAbi() { + return secondaryCpuAbi; + } + + @Override + public boolean isUse32BitAbi() { + return use32BitAbi; + } + + @Override + public boolean isForceQueryable() { + return forceQueryable; + } + + @Override + public String getCodePath() { + return codePath; + } + + @Override + public String getNativeLibraryDir() { + return nativeLibraryDir; + } + + @Override + public String getNativeLibraryRootDir() { + return nativeLibraryRootDir; + } + + @Override + public boolean isNativeLibraryRootRequiresIsa() { + return nativeLibraryRootRequiresIsa; + } + + // TODO(b/135203078): Does nothing, remove? + @Override + public int getPreferredOrder() { + return preferredOrder; + } + + @Override + public long getLongVersionCode() { + return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); + } + + @Override + public PackageImpl setIsOverlay(boolean isOverlay) { + this.privateFlags = isOverlay + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY; + return this; + } + + @Override + public PackageImpl setExternalStorage(boolean externalStorage) { + this.flags = externalStorage + ? this.flags | ApplicationInfo.FLAG_EXTERNAL_STORAGE + : this.flags & ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; + return this; + } + + @Override + public PackageImpl setIsolatedSplitLoading(boolean isolatedSplitLoading) { + this.privateFlags = isolatedSplitLoading + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; + return this; + } + + @Override + public PackageImpl sortActivities() { + Collections.sort(this.activities, (a1, a2) -> Integer.compare(a2.order, a1.order)); + return this; + } + + @Override + public PackageImpl sortReceivers() { + Collections.sort(this.receivers, (a1, a2) -> Integer.compare(a2.order, a1.order)); + return this; + } + + @Override + public PackageImpl sortServices() { + Collections.sort(this.services, (a1, a2) -> Integer.compare(a2.order, a1.order)); + return this; + } + + @Override + public PackageImpl setBaseRevisionCode(int baseRevisionCode) { + this.baseRevisionCode = baseRevisionCode; + return this; + } + + @Override + public PackageImpl setPreferredOrder(int preferredOrder) { + this.preferredOrder = preferredOrder; + return this; + } + + @Override + public PackageImpl setVersionName(String versionName) { + this.versionName = TextUtils.safeIntern(versionName); + return this; + } + + @Override + public ParsingPackage setCompileSdkVersion(int compileSdkVersion) { + this.compileSdkVersion = compileSdkVersion; + return this; + } + + @Override + public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) { + this.compileSdkVersionCodename = TextUtils.safeIntern(compileSdkVersionCodename); + return this; + } + + @Override + public PackageImpl setMaxAspectRatio(float maxAspectRatio) { + this.maxAspectRatio = maxAspectRatio; + return this; + } + + @Override + public PackageImpl setMinAspectRatio(float minAspectRatio) { + this.minAspectRatio = minAspectRatio; + return this; + } + + @Override + public PackageImpl setMinSdkVersion(int minSdkVersion) { + this.minSdkVersion = minSdkVersion; + return this; + } + + @Override + public PackageImpl setTargetSdkVersion(int targetSdkVersion) { + this.targetSdkVersion = targetSdkVersion; + return this; + } + + @Override + public PackageImpl setRealPackage(String realPackage) { + this.realPackage = realPackage; + return this; + } + + @Override + public PackageImpl addConfigPreference(ConfigurationInfo configPreference) { + this.configPreferences = ArrayUtils.add(this.configPreferences, configPreference); + return this; + } + + @Override + public PackageImpl addReqFeature(FeatureInfo reqFeature) { + this.reqFeatures = ArrayUtils.add(this.reqFeatures, reqFeature); + return this; + } + + @Override + public PackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) { + this.featureGroups = ArrayUtils.add(this.featureGroups, featureGroup); + return this; + } + + @Override + public PackageImpl addProtectedBroadcast(String protectedBroadcast) { + if (this.protectedBroadcasts == null + || !this.protectedBroadcasts.contains(protectedBroadcast)) { + this.protectedBroadcasts = ArrayUtils.add(this.protectedBroadcasts, + TextUtils.safeIntern(protectedBroadcast)); + } + return this; + } + + @Override + public PackageImpl addInstrumentation(ParsedInstrumentation instrumentation) { + this.instrumentations = ArrayUtils.add(this.instrumentations, instrumentation); + return this; + } + + @Override + public PackageImpl addOriginalPackage(String originalPackage) { + this.originalPackages = ArrayUtils.add(this.originalPackages, originalPackage); + return this; + } + + @Override + public PackageImpl addAdoptPermission(String adoptPermission) { + this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission); + return this; + } + + @Override + public PackageImpl addPermission(ParsedPermission permission) { + this.permissions = ArrayUtils.add(this.permissions, permission); + return this; + } + + @Override + public PackageImpl removePermission(int index) { + this.permissions.remove(index); + return this; + } + + @Override + public PackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) { + this.permissionGroups = ArrayUtils.add(this.permissionGroups, permissionGroup); + return this; + } + + @Override + public PackageImpl addRequestedPermission(String permission) { + this.requestedPermissions = ArrayUtils.add(this.requestedPermissions, + TextUtils.safeIntern(permission)); + return this; + } + + @Override + public PackageImpl addImplicitPermission(String permission) { + this.implicitPermissions = ArrayUtils.add(this.implicitPermissions, + TextUtils.safeIntern(permission)); + return this; + } + + @Override + public PackageImpl addKeySet(String keySetName, PublicKey publicKey) { + if (keySetMapping == null) { + keySetMapping = new ArrayMap<>(); + } + + ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName); + if (publicKeys == null) { + publicKeys = new ArraySet<>(); + keySetMapping.put(keySetName, publicKeys); + } + + publicKeys.add(publicKey); + + return this; + } + + @Override + public ParsingPackage addActivity(ParsedActivity parsedActivity) { + this.activities = ArrayUtils.add(this.activities, parsedActivity); + return this; + } + + @Override + public ParsingPackage addReceiver(ParsedActivity parsedReceiver) { + this.receivers = ArrayUtils.add(this.receivers, parsedReceiver); + return this; + } + + @Override + public ParsingPackage addService(ParsedService parsedService) { + this.services = ArrayUtils.add(this.services, parsedService); + return this; + } + + @Override + public ParsingPackage addProvider(ParsedProvider parsedProvider) { + this.providers = ArrayUtils.add(this.providers, parsedProvider); + return this; + } + + @Override + public PackageImpl addLibraryName(String libraryName) { + this.libraryNames = ArrayUtils.add(this.libraryNames, TextUtils.safeIntern(libraryName)); + return this; + } + + @Override + public PackageImpl addUsesLibrary(String libraryName) { + this.usesLibraries = ArrayUtils.add(this.usesLibraries, TextUtils.safeIntern(libraryName)); + return this; + } + + @Override + public PackageImpl addUsesOptionalLibrary(String libraryName) { + this.usesOptionalLibraries = ArrayUtils.add(this.usesOptionalLibraries, + TextUtils.safeIntern(libraryName)); + return this; + } + + @Override + public PackageImpl removeUsesOptionalLibrary(String libraryName) { + this.usesOptionalLibraries = ArrayUtils.remove(this.usesOptionalLibraries, libraryName); + return this; + } + + @Override + public PackageImpl addUsesStaticLibrary(String libraryName) { + this.usesStaticLibraries = ArrayUtils.add(this.usesStaticLibraries, + TextUtils.safeIntern(libraryName)); + return this; + } + + @Override + public PackageImpl addUsesStaticLibraryVersion(long version) { + this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions, + version, true); + return this; + } + + @Override + public PackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) { + this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class, + this.usesStaticLibrariesCertDigests, certSha256Digests, true); + return this; + } + + @Override + public PackageImpl addPreferredActivityFilter( + ParsedActivityIntentInfo parsedActivityIntentInfo) { + this.preferredActivityFilters = ArrayUtils.add(this.preferredActivityFilters, + parsedActivityIntentInfo); + return this; + } + + @Override + public PackageImpl addQueriesIntent(Intent intent) { + this.queriesIntents = ArrayUtils.add(this.queriesIntents, intent); + return this; + } + + @Override + public PackageImpl addQueriesPackage(String packageName) { + this.queriesPackages = ArrayUtils.add(this.queriesPackages, + TextUtils.safeIntern(packageName)); + return this; + } + + @Override + public PackageImpl setSupportsSmallScreens(int supportsSmallScreens) { + if (supportsSmallScreens == 1) { + return this; + } + + this.supportsSmallScreens = supportsSmallScreens; + return this; + } + + @Override + public PackageImpl setSupportsNormalScreens(int supportsNormalScreens) { + if (supportsNormalScreens == 1) { + return this; + } + + this.supportsNormalScreens = supportsNormalScreens; + return this; + } + + @Override + public PackageImpl setSupportsLargeScreens(int supportsLargeScreens) { + if (supportsLargeScreens == 1) { + return this; + } + + this.supportsLargeScreens = supportsLargeScreens; + return this; + } + + @Override + public PackageImpl setSupportsXLargeScreens(int supportsXLargeScreens) { + if (supportsXLargeScreens == 1) { + return this; + } + + this.supportsXLargeScreens = supportsXLargeScreens; + return this; + } + + @Override + public PackageImpl setResizeable(int resizeable) { + if (resizeable == 1) { + return this; + } + + this.resizeable = resizeable; + return this; + } + + @Override + public PackageImpl setAnyDensity(int anyDensity) { + if (anyDensity == 1) { + return this; + } + + this.anyDensity = anyDensity; + return this; + } + + @Override + public PackageImpl setRequiresSmallestWidthDp(int requiresSmallestWidthDp) { + this.requiresSmallestWidthDp = requiresSmallestWidthDp; + return this; + } + + @Override + public PackageImpl setCompatibleWidthLimitDp(int compatibleWidthLimitDp) { + this.compatibleWidthLimitDp = compatibleWidthLimitDp; + return this; + } + + @Override + public PackageImpl setLargestWidthLimitDp(int largestWidthLimitDp) { + this.largestWidthLimitDp = largestWidthLimitDp; + return this; + } + + @Override + public PackageImpl setInstallLocation(int installLocation) { + this.installLocation = installLocation; + return this; + } + + @Override + public PackageImpl setTargetSandboxVersion(int targetSandboxVersion) { + this.targetSandboxVersion = targetSandboxVersion; + return this; + } + + @Override + public PackageImpl setRequiredForAllUsers(boolean requiredForAllUsers) { + this.requiredForAllUsers = requiredForAllUsers; + return this; + } + + @Override + public PackageImpl setRestrictedAccountType(String restrictedAccountType) { + this.restrictedAccountType = restrictedAccountType; + return this; + } + + @Override + public PackageImpl setRequiredAccountType(String requiredAccountType) { + this.requiredAccountType = requiredAccountType; + return this; + } + + @Override + public PackageImpl setBaseHardwareAccelerated(boolean baseHardwareAccelerated) { + this.baseHardwareAccelerated = baseHardwareAccelerated; + + this.flags = baseHardwareAccelerated + ? this.flags | ApplicationInfo.FLAG_HARDWARE_ACCELERATED + : this.flags & ~ApplicationInfo.FLAG_HARDWARE_ACCELERATED; + + return this; + } + + @Override + public PackageImpl setHasDomainUrls(boolean hasDomainUrls) { + this.privateFlags = hasDomainUrls + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS; + return this; + } + + @Override + public PackageImpl setAppMetaData(Bundle appMetaData) { + this.appMetaData = appMetaData; + return this; + } + + @Override + public PackageImpl setOverlayTarget(String overlayTarget) { + this.overlayTarget = overlayTarget; + return this; + } + + @Override + public PackageImpl setOverlayTargetName(String overlayTargetName) { + this.overlayTargetName = overlayTargetName; + return this; + } + + @Override + public PackageImpl setOverlayCategory(String overlayCategory) { + this.overlayCategory = overlayCategory; + return this; + } + + @Override + public PackageImpl setOverlayPriority(int overlayPriority) { + this.overlayPriority = overlayPriority; + return this; + } + + @Override + public PackageImpl setOverlayIsStatic(boolean overlayIsStatic) { + this.overlayIsStatic = overlayIsStatic; + return this; + } + + @Override + public PackageImpl setStaticSharedLibName(String staticSharedLibName) { + this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName); + return this; + } + + @Override + public PackageImpl setStaticSharedLibVersion(long staticSharedLibVersion) { + this.staticSharedLibVersion = staticSharedLibVersion; + return this; + } + + @Override + public PackageImpl setSharedUserId(String sharedUserId) { + this.sharedUserId = TextUtils.safeIntern(sharedUserId); + return this; + } + + @Override + public PackageImpl setSharedUserLabel(int sharedUserLabel) { + this.sharedUserLabel = sharedUserLabel; + return this; + } + + @Override + public PackageImpl setRestrictUpdateHash(byte[] restrictUpdateHash) { + this.restrictUpdateHash = restrictUpdateHash; + return this; + } + + @Override + public PackageImpl setUpgradeKeySets(ArraySet<String> upgradeKeySets) { + this.upgradeKeySets = upgradeKeySets; + return this; + } + + @Override + public PackageImpl setVolumeUuid(String volumeUuid) { + this.volumeUuid = volumeUuid; + return this; + } + + @Deprecated + @Override + public PackageImpl setApplicationVolumeUuid(String applicationVolumeUuid) { + this.applicationVolumeUuid = applicationVolumeUuid; + return this; + } + + @Override + public PackageImpl setSigningDetails(PackageParser.SigningDetails signingDetails) { + this.signingDetails = signingDetails; + return this; + } + + @Override + public PackageImpl setCodePath(String codePath) { + this.codePath = codePath; + return this; + } + + @Override + public PackageImpl setUse32BitAbi(boolean use32BitAbi) { + this.use32BitAbi = use32BitAbi; + return this; + } + + @Override + public PackageImpl setCpuAbiOverride(String cpuAbiOverride) { + this.cpuAbiOverride = cpuAbiOverride; + return this; + } + + @Override + public PackageImpl setForceQueryable(boolean forceQueryable) { + this.forceQueryable = forceQueryable; + return this; + } + + // TODO(b/135203078): Remove and move PackageManagerService#renameStaticSharedLibraryPackage + // into initial package parsing + @Override + public PackageImpl setPackageName(String packageName) { + this.packageName = packageName.intern(); + + if (permissions != null) { + for (ParsedPermission permission : permissions) { + permission.setPackageName(this.packageName); + } + } + + if (permissionGroups != null) { + for (ParsedPermissionGroup permissionGroup : permissionGroups) { + permissionGroup.setPackageName(this.packageName); + } + } + + if (activities != null) { + for (ParsedActivity parsedActivity : activities) { + parsedActivity.setPackageName(this.packageName); + } + } + + if (receivers != null) { + for (ParsedActivity receiver : receivers) { + receiver.setPackageName(this.packageName); + } + } + + if (providers != null) { + for (ParsedProvider provider : providers) { + provider.setPackageName(this.packageName); + } + } + + if (services != null) { + for (ParsedService service : services) { + service.setPackageName(this.packageName); + } + } + + if (instrumentations != null) { + for (ParsedInstrumentation instrumentation : instrumentations) { + instrumentation.setPackageName(this.packageName); + } + } + + return this; + } + + // Under this is parseBaseApplication + + @Override + public PackageImpl setAllowBackup(boolean allowBackup) { + this.flags = allowBackup + ? this.flags | ApplicationInfo.FLAG_ALLOW_BACKUP + : this.flags & ~ApplicationInfo.FLAG_ALLOW_BACKUP; + return this; + } + + @Override + public PackageImpl setKillAfterRestore(boolean killAfterRestore) { + this.flags = killAfterRestore + ? this.flags | ApplicationInfo.FLAG_KILL_AFTER_RESTORE + : this.flags & ~ApplicationInfo.FLAG_KILL_AFTER_RESTORE; + return this; + } + + @Override + public PackageImpl setRestoreAnyVersion(boolean restoreAnyVersion) { + this.flags = restoreAnyVersion + ? this.flags | ApplicationInfo.FLAG_RESTORE_ANY_VERSION + : this.flags & ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION; + return this; + } + + @Override + public PackageImpl setFullBackupOnly(boolean fullBackupOnly) { + this.flags = fullBackupOnly + ? this.flags | ApplicationInfo.FLAG_FULL_BACKUP_ONLY + : this.flags & ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY; + return this; + } + + @Override + public PackageImpl setPersistent(boolean persistent) { + this.flags = persistent + ? this.flags | ApplicationInfo.FLAG_PERSISTENT + : this.flags & ~ApplicationInfo.FLAG_PERSISTENT; + return this; + } + + @Override + public PackageImpl setDebuggable(boolean debuggable) { + this.flags = debuggable + ? this.flags | ApplicationInfo.FLAG_DEBUGGABLE + : this.flags & ~ApplicationInfo.FLAG_DEBUGGABLE; + return this; + } + + @Override + public PackageImpl setProfileableByShell(boolean profileableByShell) { + this.privateFlags = profileableByShell + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL; + return this; + } + + @Override + public PackageImpl setVmSafeMode(boolean vmSafeMode) { + this.flags = vmSafeMode + ? this.flags | ApplicationInfo.FLAG_VM_SAFE_MODE + : this.flags & ~ApplicationInfo.FLAG_VM_SAFE_MODE; + return this; + } + + @Override + public PackageImpl setHasCode(boolean hasCode) { + this.flags = hasCode + ? this.flags | ApplicationInfo.FLAG_HAS_CODE + : this.flags & ~ApplicationInfo.FLAG_HAS_CODE; + return this; + } + + @Override + public PackageImpl setAllowTaskReparenting(boolean allowTaskReparenting) { + this.flags = allowTaskReparenting + ? this.flags | ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING + : this.flags & ~ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING; + return this; + } + + @Override + public PackageImpl setAllowClearUserData(boolean allowClearUserData) { + this.flags = allowClearUserData + ? this.flags | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA + : this.flags & ~ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA; + return this; + } + + @Override + public PackageImpl setLargeHeap(boolean largeHeap) { + this.flags = largeHeap + ? this.flags | ApplicationInfo.FLAG_LARGE_HEAP + : this.flags & ~ApplicationInfo.FLAG_LARGE_HEAP; + return this; + } + + @Override + public PackageImpl setUsesCleartextTraffic(boolean usesCleartextTraffic) { + this.flags = usesCleartextTraffic + ? this.flags | ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC + : this.flags & ~ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC; + return this; + } + + @Override + public PackageImpl setSupportsRtl(boolean supportsRtl) { + this.flags = supportsRtl + ? this.flags | ApplicationInfo.FLAG_SUPPORTS_RTL + : this.flags & ~ApplicationInfo.FLAG_SUPPORTS_RTL; + return this; + } + + @Override + public PackageImpl setTestOnly(boolean testOnly) { + this.flags = testOnly + ? this.flags | ApplicationInfo.FLAG_TEST_ONLY + : this.flags & ~ApplicationInfo.FLAG_TEST_ONLY; + return this; + } + + @Override + public PackageImpl setMultiArch(boolean multiArch) { + this.flags = multiArch + ? this.flags | ApplicationInfo.FLAG_MULTIARCH + : this.flags & ~ApplicationInfo.FLAG_MULTIARCH; + return this; + } + + @Override + public PackageImpl setExtractNativeLibs(boolean extractNativeLibs) { + this.flags = extractNativeLibs + ? this.flags | ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS + : this.flags & ~ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS; + return this; + } + + @Override + public PackageImpl setIsGame(boolean isGame) { + this.flags = isGame + ? this.flags | ApplicationInfo.FLAG_IS_GAME + : this.flags & ~ApplicationInfo.FLAG_IS_GAME; + return this; + } + + @Override + public PackageImpl setBackupInForeground(boolean backupInForeground) { + this.privateFlags = backupInForeground + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND; + return this; + } + + @Override + public PackageImpl setUseEmbeddedDex(boolean useEmbeddedDex) { + this.privateFlags = useEmbeddedDex + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX; + return this; + } + + @Override + public PackageImpl setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage) { + this.privateFlags = defaultToDeviceProtectedStorage + ? this.privateFlags | ApplicationInfo + .PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE + : this.privateFlags & ~ApplicationInfo + .PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; + return this; + } + + @Override + public PackageImpl setDirectBootAware(boolean directBootAware) { + this.privateFlags = directBootAware + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; + return this; + } + + @Override + public PackageImpl setPartiallyDirectBootAware(boolean partiallyDirectBootAware) { + this.privateFlags = partiallyDirectBootAware + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE; + return this; + } + + @Override + public PackageImpl setActivitiesResizeModeResizeableViaSdkVersion( + boolean resizeableViaSdkVersion + ) { + this.privateFlags = resizeableViaSdkVersion + ? this.privateFlags | ApplicationInfo + .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION + : this.privateFlags & ~ApplicationInfo + .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; + return this; + } + + @Override + public PackageImpl setActivitiesResizeModeResizeable(boolean resizeable) { + this.privateFlags = resizeable + ? this.privateFlags | ApplicationInfo + .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE + : this.privateFlags & ~ApplicationInfo + .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE; + + this.privateFlags = !resizeable + ? this.privateFlags | ApplicationInfo + .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE + : this.privateFlags & ~ApplicationInfo + .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; + return this; + } + + @Override + public PackageImpl setAllowClearUserDataOnFailedRestore( + boolean allowClearUserDataOnFailedRestore + ) { + this.privateFlags = allowClearUserDataOnFailedRestore + ? this.privateFlags | ApplicationInfo + .PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE + : this.privateFlags & ~ApplicationInfo + .PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE; + return this; + } + + @Override + public PackageImpl setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture) { + this.privateFlags = allowAudioPlaybackCapture + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; + return this; + } + + @Override + public PackageImpl setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage) { + this.privateFlags = requestLegacyExternalStorage + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE; + return this; + } + + @Override + public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) { + this.privateFlags = usesNonSdkApi + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API; + return this; + } + + @Override + public PackageImpl setHasFragileUserData(boolean hasFragileUserData) { + this.privateFlags = hasFragileUserData + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA; + return this; + } + + @Override + public PackageImpl setCantSaveState(boolean cantSaveState) { + this.privateFlags = cantSaveState + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE; + return this; + } + + @Override + public boolean cantSaveState() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0; + } + + @Override + public boolean isLibrary() { + return staticSharedLibName != null || !ArrayUtils.isEmpty(libraryNames); + } + + // TODO(b/135203078): This does nothing until the final stage without applyPolicy being + // part of PackageParser + @Override + public boolean isSystemApp() { + return (flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + // TODO(b/135203078): This does nothing until the final stage without applyPolicy being + // part of PackageParser + @Override + public boolean isSystemExt() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0; + } + + // TODO(b/135203078): This does nothing until the final stage without applyPolicy being + // part of PackageParser + @Override + public boolean isUpdatedSystemApp() { + return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + } + + @Override + public PackageImpl setStaticSharedLibrary(boolean staticSharedLibrary) { + this.privateFlags = staticSharedLibrary + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY; + return this; + } + + @Override + public boolean isStaticSharedLibrary() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0; + } + + @Override + public PackageImpl setVisibleToInstantApps(boolean visibleToInstantApps) { + this.visibleToInstantApps = visibleToInstantApps; + return this; + } + + @Override + public PackageImpl setIconRes(int iconRes) { + this.iconRes = iconRes; + return this; + } + + @Override + public PackageImpl setRoundIconRes(int roundIconRes) { + this.roundIconRes = roundIconRes; + return this; + } + + @Override + public PackageImpl setClassName(String className) { + this.className = className; + return this; + } + + @Override + public PackageImpl setManageSpaceActivityName(String manageSpaceActivityName) { + this.manageSpaceActivityName = manageSpaceActivityName; + return this; + } + + @Override + public PackageImpl setBackupAgentName(String backupAgentName) { + this.backupAgentName = backupAgentName; + return this; + } + + @Override + public PackageImpl setFullBackupContent(int fullBackupContent) { + this.fullBackupContent = fullBackupContent; + return this; + } + + @Override + public PackageImpl setTheme(int theme) { + this.theme = theme; + return this; + } + + @Override + public PackageImpl setDescriptionRes(int descriptionRes) { + this.descriptionRes = descriptionRes; + return this; + } + + @Override + public PackageImpl setNetworkSecurityConfigRes(int networkSecurityConfigRes) { + this.networkSecurityConfigRes = networkSecurityConfigRes; + return this; + } + + @Override + public PackageImpl setCategory(int category) { + this.category = category; + return this; + } + + @Override + public PackageImpl setPermission(String permission) { + this.permission = permission; + return this; + } + + @Override + public PackageImpl setTaskAffinity(String taskAffinity) { + this.taskAffinity = taskAffinity; + return this; + } + + @Override + public PackageImpl setAppComponentFactory(String appComponentFactory) { + this.appComponentFactory = appComponentFactory; + return this; + } + + @Override + public PackageImpl setProcessName(String processName) { + if (processName == null) { + this.processName = packageName; + } else { + this.processName = processName; + } + return this; + } + + @Override + public PackageImpl setEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + @Override + public PackageImpl setUiOptions(int uiOptions) { + this.uiOptions = uiOptions; + return this; + } + + @Override + public PackageImpl setClassLoaderName(String classLoaderName) { + this.classLoaderName = classLoaderName; + return this; + } + + @Override + public PackageImpl setZygotePreloadName(String zygotePreloadName) { + this.zygotePreloadName = zygotePreloadName; + return this; + } + + // parsePackageItemInfo + + @Override + public String getName() { + return name; + } + + @Override + public PackageImpl setName(String name) { + this.name = name; + return this; + } + + @Override + public PackageImpl setIcon(int icon) { + this.icon = icon; + return this; + } + + @Override + public PackageImpl setNonLocalizedLabel(CharSequence nonLocalizedLabel) { + this.nonLocalizedLabel = nonLocalizedLabel; + return this; + } + + @Override + public PackageImpl setLogo(int logo) { + this.logo = logo; + return this; + } + + @Override + public PackageImpl setBanner(int banner) { + this.banner = banner; + return this; + } + + @Override + public PackageImpl setLabelRes(int labelRes) { + this.labelRes = labelRes; + return this; + } + + @Override + public PackageImpl asSplit( + String[] splitNames, + String[] splitCodePaths, + int[] splitRevisionCodes, + SparseArray<int[]> splitDependencies + ) { + this.splitNames = splitNames; + + if (this.splitNames != null) { + for (int index = 0; index < this.splitNames.length; index++) { + splitNames[index] = TextUtils.safeIntern(splitNames[index]); + } + } + + this.splitCodePaths = splitCodePaths; + this.splitRevisionCodes = splitRevisionCodes; + this.splitDependencies = splitDependencies; + + int count = splitNames.length; + this.splitFlags = new int[count]; + this.splitClassLoaderNames = new String[count]; + return this; + } + + @Override + public String[] getSplitNames() { + return splitNames; + } + + @Override + public String[] getSplitCodePaths() { + return splitCodePaths; + } + + @Override + public PackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) { + this.splitFlags[splitIndex] = splitHasCode + ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE + : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE; + return this; + } + + @Override + public PackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) { + this.splitClassLoaderNames[splitIndex] = classLoaderName; + return this; + } + + @Override + public List<String> makeListAllCodePaths() { + ArrayList<String> paths = new ArrayList<>(); + paths.add(baseCodePath); + + if (!ArrayUtils.isEmpty(splitCodePaths)) { + Collections.addAll(paths, splitCodePaths); + } + return paths; + } + + @Override + public PackageImpl setBaseCodePath(String baseCodePath) { + this.baseCodePath = baseCodePath; + return this; + } + + @Override + public PackageImpl setSplitCodePaths(String[] splitCodePaths) { + this.splitCodePaths = splitCodePaths; + return this; + } + + @Override + public String toString() { + return "Package{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + packageName + "}"; + } + + @Override + public PackageImpl setPrimaryCpuAbi(String primaryCpuAbi) { + this.primaryCpuAbi = primaryCpuAbi; + return this; + } + + @Override + public PackageImpl setSecondaryCpuAbi(String secondaryCpuAbi) { + this.secondaryCpuAbi = secondaryCpuAbi; + return this; + } + + @Override + public PackageImpl setNativeLibraryRootDir(String nativeLibraryRootDir) { + this.nativeLibraryRootDir = nativeLibraryRootDir; + return this; + } + + @Override + public PackageImpl setNativeLibraryRootRequiresIsa(boolean nativeLibraryRootRequiresIsa) { + this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + return this; + } + + @Override + public PackageImpl setNativeLibraryDir(String nativeLibraryDir) { + this.nativeLibraryDir = nativeLibraryDir; + return this; + } + + @Override + public PackageImpl setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir) { + this.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + return this; + } + + @Deprecated + @Override + public PackageImpl setApplicationInfoCodePath(String applicationInfoCodePath) { + this.applicationInfoCodePath = applicationInfoCodePath; + return this; + } + + @Deprecated + @Override + public PackageImpl setApplicationInfoResourcePath(String applicationInfoResourcePath) { + this.applicationInfoResourcePath = applicationInfoResourcePath; + return this; + } + + @Deprecated + @Override + public PackageImpl setApplicationInfoBaseResourcePath( + String applicationInfoBaseResourcePath) { + this.applicationInfoBaseResourcePath = applicationInfoBaseResourcePath; + return this; + } + + @Deprecated + @Override + public PackageImpl setApplicationInfoSplitResourcePaths( + String[] applicationInfoSplitResourcePaths) { + this.applicationInfoSplitResourcePaths = applicationInfoSplitResourcePaths; + return this; + } + + @Override + public boolean isDirectBootAware() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0; + } + + @Override + public PackageImpl setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware) { + if (activities != null) { + for (ParsedActivity parsedActivity : activities) { + parsedActivity.directBootAware = allComponentsDirectBootAware; + } + } + + if (receivers != null) { + for (ParsedActivity parsedReceiver : receivers) { + parsedReceiver.directBootAware = allComponentsDirectBootAware; + } + } + + if (providers != null) { + for (ParsedProvider parsedProvider : providers) { + parsedProvider.directBootAware = allComponentsDirectBootAware; + } + } + + if (services != null) { + for (ParsedService parsedService : services) { + parsedService.directBootAware = allComponentsDirectBootAware; + } + } + + return this; + } + + @Override + public PackageImpl setSystem(boolean system) { + this.flags = system + ? this.flags | ApplicationInfo.FLAG_SYSTEM + : this.flags & ~ApplicationInfo.FLAG_SYSTEM; + return this; + } + + @Override + public PackageImpl setSystemExt(boolean systemExt) { + this.privateFlags = systemExt + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT; + return this; + } + + @Override + public PackageImpl setIsStub(boolean isStub) { + this.isStub = isStub; + return this; + } + + @Override + public PackageImpl setCoreApp(boolean coreApp) { + this.coreApp = coreApp; + return this; + } + + @Override + public ParsedPackage capPermissionPriorities() { + if (permissionGroups != null && !permissionGroups.isEmpty()) { + for (int i = permissionGroups.size() - 1; i >= 0; --i) { + // TODO(b/135203078): Builder/immutability + permissionGroups.get(i).priority = 0; + } + } + return this; + } + + @Override + public ParsedPackage clearProtectedBroadcasts() { + if (protectedBroadcasts != null) { + protectedBroadcasts.clear(); + } + return this; + } + + @Override + public ParsedPackage markNotActivitiesAsNotExportedIfSingleUser() { + // ignore export request for single user receivers + if (receivers != null) { + for (ParsedActivity receiver : receivers) { + if ((receiver.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) { + receiver.exported = false; + } + } + } + // ignore export request for single user services + if (services != null) { + for (ParsedService service : services) { + if ((service.flags & ServiceInfo.FLAG_SINGLE_USER) != 0) { + service.exported = false; + } + } + } + // ignore export request for single user providers + if (providers != null) { + for (ParsedProvider provider : providers) { + if ((provider.flags & ProviderInfo.FLAG_SINGLE_USER) != 0) { + provider.exported = false; + } + } + } + + return this; + } + + @Override + public ParsedPackage setPrivileged(boolean privileged) { + this.privateFlags = privileged + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PRIVILEGED + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + return this; + } + + @Override + public ParsedPackage setOem(boolean oem) { + this.privateFlags = oem + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_OEM + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_OEM; + return this; + } + + @Override + public ParsedPackage setVendor(boolean vendor) { + this.privateFlags = vendor + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_VENDOR + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_VENDOR; + return this; + } + + @Override + public ParsedPackage setProduct(boolean product) { + this.privateFlags = product + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PRODUCT + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PRODUCT; + return this; + } + + @Override + public ParsedPackage setOdm(boolean odm) { + this.privateFlags = odm + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ODM + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ODM; + return this; + } + + @Override + public ParsedPackage setSignedWithPlatformKey(boolean signedWithPlatformKey) { + this.privateFlags = signedWithPlatformKey + ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY + : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY; + return this; + } + + @Override + public ParsedPackage clearOriginalPackages() { + if (originalPackages != null) { + originalPackages.clear(); + } + return this; + } + + @Override + public ParsedPackage clearAdoptPermissions() { + if (adoptPermissions != null) { + adoptPermissions.clear(); + } + return this; + } + + @Override + public PackageImpl addUsesLibrary(int index, String libraryName) { + this.usesLibraries = ArrayUtils.add(usesLibraries, index, libraryName); + return this; + } + + @Override + public ParsedPackage removeUsesLibrary(String libraryName) { + this.usesLibraries = ArrayUtils.remove(this.usesLibraries, libraryName); + return this; + } + + @Override + public PackageImpl addUsesOptionalLibrary(int index, String libraryName) { + this.usesOptionalLibraries = ArrayUtils.add(usesOptionalLibraries, index, libraryName); + return this; + } + + @Nullable + @Override + public List<String> getUsesOptionalLibraries() { + return usesOptionalLibraries; + } + + @Override + public int getVersionCode() { + return versionCode; + } + + @Nullable + @Override + public long[] getUsesStaticLibrariesVersions() { + return usesStaticLibrariesVersions; + } + + @Override + public PackageImpl setPackageSettingCallback(PackageSettingCallback packageSettingCallback) { + packageSettingCallback.setAndroidPackage(this); + return this; + } + + @Override + public PackageImpl setUpdatedSystemApp(boolean updatedSystemApp) { + this.flags = updatedSystemApp + ? this.flags | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP + : this.flags & ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + return this; + } + + @Override + public boolean isPrivileged() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; + } + + @Override + public PackageImpl setSeInfo(String seInfo) { + this.seInfo = seInfo; + return this; + } + + @Override + public PackageImpl setSeInfoUser(String seInfoUser) { + this.seInfoUser = seInfoUser; + return this; + } + + @Override + public PackageImpl initForUser(int userId) { + // TODO(b/135203078): Move this user state to some other data structure + this.uid = UserHandle.getUid(userId, UserHandle.getAppId(this.uid)); + + if ("android".equals(packageName)) { + dataDir = Environment.getDataSystemDirectory().getAbsolutePath(); + return this; + } + + deviceProtectedDataDir = Environment + .getDataUserDePackageDirectory(applicationVolumeUuid, userId, packageName) + .getAbsolutePath(); + credentialProtectedDataDir = Environment + .getDataUserCePackageDirectory(applicationVolumeUuid, userId, packageName) + .getAbsolutePath(); + + if ((privateFlags & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0 + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + dataDir = deviceProtectedDataDir; + } else { + dataDir = credentialProtectedDataDir; + } + return this; + } + + @Override + public ParsedPackage setFactoryTest(boolean factoryTest) { + this.flags = factoryTest + ? this.flags | ApplicationInfo.FLAG_FACTORY_TEST + : this.flags & ~ApplicationInfo.FLAG_FACTORY_TEST; + return this; + } + + @Override + public String getManifestPackageName() { + return manifestPackageName; + } + + @Override + public String getRealPackage() { + return realPackage; + } + + @Override + public String getOverlayTarget() { + return overlayTarget; + } + + @Override + public String getOverlayTargetName() { + return overlayTargetName; + } + + @Override + public boolean isOverlayIsStatic() { + return overlayIsStatic; + } + + @Override + public int[] getSplitFlags() { + return splitFlags; + } + + @Deprecated + @Override + public String getApplicationInfoVolumeUuid() { + return applicationVolumeUuid; + } + + @Nullable + @Override + public List<String> getProtectedBroadcasts() { + return protectedBroadcasts; + } + + @Nullable + @Override + public Set<String> getUpgradeKeySets() { + return upgradeKeySets; + } + + @Nullable + @Override + public String[][] getUsesStaticLibrariesCertDigests() { + return usesStaticLibrariesCertDigests; + } + + @Override + public int getOverlayPriority() { + return overlayPriority; + } + + @Deprecated + @Override + public String getAppInfoPackageName() { + return packageName; + } + + @Override + public UUID getStorageUuid() { + return StorageManager.convert(applicationVolumeUuid); + } + + @Override + public int getUid() { + return uid; + } + + @Override + public boolean isStub() { + return isStub; + } + + @Deprecated + @Override + public String getAppInfoCodePath() { + return applicationInfoCodePath; + } + + @Override + public boolean isSystem() { + return (flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + @Override + public boolean isMatch(int flags) { + if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { + return isSystem(); + } + return true; + } + + @Override + public boolean isVisibleToInstantApps() { + return visibleToInstantApps; + } + + @Override + public PackageImpl setLastPackageUsageTimeInMills(int reason, long time) { + lastPackageUsageTimeInMills[reason] = time; + return this; + } + + @Override + public List<SharedLibraryInfo> getUsesLibraryInfos() { + return usesLibraryInfos; + } + + @NonNull + @Override + public List<String> getAllCodePaths() { + return makeListAllCodePaths(); + } + + @Nullable + @Override + public String[] getUsesLibraryFiles() { + return usesLibraryFiles; + } + + @Override + public PackageImpl setUsesLibraryInfos( + @Nullable List<SharedLibraryInfo> usesLibraryInfos) { + this.usesLibraryInfos = usesLibraryInfos; + return this; + } + + @Override + public PackageImpl setUsesLibraryFiles(@Nullable String[] usesLibraryFiles) { + this.usesLibraryFiles = usesLibraryFiles; + return this; + } + + @Override + public PackageImpl setUid(int uid) { + this.uid = uid; + return this; + } + + @Override + public List<String> getAdoptPermissions() { + return adoptPermissions; + } + + @Override + public ApplicationInfo toAppInfoWithoutState() { + updateFlags(); + + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = packageName; + appInfo.flags = flags; + appInfo.privateFlags = privateFlags; + + appInfo.appComponentFactory = appComponentFactory; + appInfo.backupAgentName = backupAgentName; + appInfo.banner = banner; + appInfo.category = category; + appInfo.classLoaderName = classLoaderName; + appInfo.className = className; + appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp; + appInfo.compileSdkVersion = compileSdkVersion; + appInfo.compileSdkVersionCodename = compileSdkVersionCodename; + appInfo.credentialProtectedDataDir = credentialProtectedDataDir; + appInfo.dataDir = dataDir; + appInfo.descriptionRes = descriptionRes; + appInfo.deviceProtectedDataDir = deviceProtectedDataDir; + appInfo.enabled = enabled; + appInfo.fullBackupContent = fullBackupContent; + appInfo.hiddenUntilInstalled = hiddenUntilInstalled; + appInfo.icon = icon; + appInfo.iconRes = iconRes; + appInfo.installLocation = installLocation; + appInfo.labelRes = labelRes; + appInfo.largestWidthLimitDp = largestWidthLimitDp; + appInfo.logo = logo; + appInfo.manageSpaceActivityName = manageSpaceActivityName; + appInfo.maxAspectRatio = maxAspectRatio; + appInfo.metaData = appMetaData; + appInfo.minAspectRatio = minAspectRatio; + appInfo.minSdkVersion = minSdkVersion; + appInfo.name = name; + if (appInfo.name != null) { + appInfo.name = appInfo.name.trim(); + } + appInfo.nativeLibraryDir = nativeLibraryDir; + appInfo.nativeLibraryRootDir = nativeLibraryRootDir; + appInfo.nativeLibraryRootRequiresIsa = 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 = primaryCpuAbi; + appInfo.processName = getProcessName(); + appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp; + appInfo.roundIconRes = roundIconRes; + appInfo.secondaryCpuAbi = secondaryCpuAbi; + appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + appInfo.seInfo = seInfo; + appInfo.seInfoUser = seInfoUser; + appInfo.sharedLibraryFiles = usesLibraryFiles; + appInfo.sharedLibraryInfos = ArrayUtils.isEmpty(usesLibraryInfos) ? null : usesLibraryInfos; + appInfo.splitClassLoaderNames = splitClassLoaderNames; + appInfo.splitDependencies = splitDependencies; + appInfo.splitNames = splitNames; + appInfo.storageUuid = StorageManager.convert(volumeUuid); + appInfo.targetSandboxVersion = targetSandboxVersion; + appInfo.targetSdkVersion = targetSdkVersion; + appInfo.taskAffinity = taskAffinity; + appInfo.theme = theme; + appInfo.uid = uid; + appInfo.uiOptions = uiOptions; + appInfo.volumeUuid = volumeUuid; + appInfo.zygotePreloadName = zygotePreloadName; + + appInfo.setBaseCodePath(baseCodePath); + appInfo.setBaseResourcePath(baseCodePath); + appInfo.setCodePath(codePath); + appInfo.setResourcePath(codePath); + appInfo.setSplitCodePaths(splitCodePaths); + appInfo.setSplitResourcePaths(splitCodePaths); + appInfo.setVersionCode(getLongVersionCode()); + + // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo. +// appInfo.showUserIcon = pkg.getShowUserIcon(); + // TODO(b/135203078): Unused? +// appInfo.resourceDirs = pkg.getResourceDirs(); + // TODO(b/135203078): Unused? +// appInfo.enabledSetting = pkg.getEnabledSetting(); + // TODO(b/135203078): See PackageImpl#getHiddenApiEnforcementPolicy +// appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy(); + + return appInfo; + } + + @Override + public PackageImpl setVersionCode(int versionCode) { + this.versionCode = versionCode; + return this; + } + + @Override + public PackageImpl setHiddenUntilInstalled(boolean hidden) { + this.hiddenUntilInstalled = hidden; + return this; + } + + @Override + public String getSeInfo() { + return seInfo; + } + + @Deprecated + @Override + public String getAppInfoResourcePath() { + return applicationInfoResourcePath; + } + + @Override + public boolean isForwardLocked() { + // TODO(b/135203078): Unused? Move to debug flag? + return false; + } + + @Override + public byte[] getRestrictUpdateHash() { + return restrictUpdateHash; + } + + @Override + public boolean hasComponentClassName(String className) { + if (activities != null) { + for (ParsedActivity parsedActivity : activities) { + if (Objects.equals(className, parsedActivity.className)) { + return true; + } + } + } + + if (receivers != null) { + for (ParsedActivity receiver : receivers) { + if (Objects.equals(className, receiver.className)) { + return true; + } + } + } + + if (providers != null) { + for (ParsedProvider provider : providers) { + if (Objects.equals(className, provider.className)) { + return true; + } + } + } + + if (services != null) { + for (ParsedService service : services) { + if (Objects.equals(className, service.className)) { + return true; + } + } + } + + if (instrumentations != null) { + for (ParsedInstrumentation instrumentation : instrumentations) { + if (Objects.equals(className, instrumentation.className)) { + return true; + } + } + } + + return false; + } + + @Override + public boolean isDefaultToDeviceProtectedStorage() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) + != 0; + } + + @Override + public boolean isInternal() { + return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0; + } + + @Override + public int getBaseRevisionCode() { + return baseRevisionCode; + } + + @Override + public int[] getSplitRevisionCodes() { + return splitRevisionCodes; + } + + @Override + public boolean canHaveOatDir() { + // The following app types CANNOT have oat directory + // - non-updated system apps + return !isSystem() || isUpdatedSystemApp(); + } + + @Override + public long getLatestPackageUseTimeInMills() { + long latestUse = 0L; + for (long use : lastPackageUsageTimeInMills) { + latestUse = Math.max(latestUse, use); + } + return latestUse; + } + + @Override + public long getLatestForegroundPackageUseTimeInMills() { + int[] foregroundReasons = { + PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY, + PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE + }; + + long latestUse = 0L; + for (int reason : foregroundReasons) { + latestUse = Math.max(latestUse, lastPackageUsageTimeInMills[reason]); + } + return latestUse; + } + + @Override + public boolean isCoreApp() { + return coreApp; + } + + @Override + public String getVersionName() { + return versionName; + } + + @Override + public PackageImpl setVersionCodeMajor(int versionCodeMajor) { + this.versionCodeMajor = versionCodeMajor; + return this; + } + + @Override + public long[] getLastPackageUsageTimeInMills() { + return lastPackageUsageTimeInMills; + } + + @Override + public String getDataDir() { + return dataDir; + } + + @Override + public boolean isExternal() { + return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; + } + + @Override + public List<String> getImplicitPermissions() { + return implicitPermissions == null ? Collections.emptyList() : implicitPermissions; + } + + /** + * TODO(b/135203078): Remove, ensure b/140256621 is fixed or irrelevant + * TODO(b/140256621): Remove after fixing instant app check + * @deprecated This method always returns false because there's no paired set method + */ + @Deprecated + @Override + public boolean isInstantApp() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; + } + + @Override + public boolean hasRequestedLegacyExternalStorage() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0; + } + + @Override + public boolean isVendor() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; + } + + @Override + public boolean isProduct() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; + } + + @Override + public boolean isOem() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0; + } + + @Override + public boolean isEncryptionAware() { + boolean isPartiallyDirectBootAware = + (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0; + return isDirectBootAware() || isPartiallyDirectBootAware; + } + + @Override + public boolean isEmbeddedDexUsed() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX) != 0; + } + + @Deprecated + @Override + public String getAppInfoProcessName() { + return processName; + } + + @Override + public List<String> getAllCodePathsExcludingResourceOnly() { + ArrayList<String> paths = new ArrayList<>(); + if ((flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { + paths.add(baseCodePath); + } + if (!ArrayUtils.isEmpty(splitCodePaths)) { + for (int i = 0; i < splitCodePaths.length; i++) { + if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { + paths.add(splitCodePaths[i]); + } + } + } + return paths; + } + + @Deprecated + @Override + public String getAppInfoName() { + return name; + } + + private boolean isSignedWithPlatformKey() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0; + } + + private boolean usesNonSdkApi() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API) != 0; + } + + private boolean isPackageWhitelistedForHiddenApis() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName); + } + + private boolean isAllowedToUseHiddenApis() { + if (isSignedWithPlatformKey()) { + return true; + } else if (isSystemApp() || isUpdatedSystemApp()) { + return usesNonSdkApi() || isPackageWhitelistedForHiddenApis(); + } else { + return false; + } + } + + @Override + public int getHiddenApiEnforcementPolicy() { + if (isAllowedToUseHiddenApis()) { + return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED; + } + + // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done + // entirely through ApplicationInfo and shouldn't touch this specific class, but that + // may not always hold true. +// if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) { +// return mHiddenApiPolicy; +// } + return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED; + } + + @Nullable + @Override + public SparseArray<int[]> getSplitDependencies() { + return splitDependencies; + } + + @Override + public boolean requestsIsolatedSplitLoading() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0; + } + + @Deprecated + @Override + public String getAppInfoClassLoaderName() { + return classLoaderName; + } + + @Override + public String getClassLoaderName() { + return classLoaderName; + } + + @Override + public String[] getSplitClassLoaderNames() { + return splitClassLoaderNames; + } + + @Override + public String getOverlayCategory() { + return overlayCategory; + } + + @Override + public boolean isProfileableByShell() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0; + } + + @Nullable + @Override + public List<ParsedActivityIntentInfo> getPreferredActivityFilters() { + return preferredActivityFilters; + } + + @Override + public boolean isHiddenUntilInstalled() { + return hiddenUntilInstalled; + } + + @Override + public int getMinSdkVersion() { + return minSdkVersion; + } + + @Override + public String getRestrictedAccountType() { + return restrictedAccountType; + } + + @Override + public String getRequiredAccountType() { + return requiredAccountType; + } + + @Override + public int getInstallLocation() { + return installLocation; + } + + @Override + public List<ParsedActivity> getReceivers() { + return receivers; + } + + @Override + public List<ParsedService> getServices() { + return services; + } + + @Override + public List<ParsedProvider> getProviders() { + return providers; + } + + @Override + public int getSharedUserLabel() { + return sharedUserLabel; + } + + @Override + public int getVersionCodeMajor() { + return versionCodeMajor; + } + + @Override + public boolean isRequiredForAllUsers() { + return requiredForAllUsers; + } + + @Override + public int getCompileSdkVersion() { + return compileSdkVersion; + } + + @Override + public String getCompileSdkVersionCodeName() { + return compileSdkVersionCodename; + } + + @Nullable + @Override + public List<ConfigurationInfo> getConfigPreferences() { + return configPreferences; + } + + @Nullable + @Override + public List<FeatureInfo> getReqFeatures() { + return reqFeatures; + } + + @Override + public List<FeatureGroupInfo> getFeatureGroups() { + return featureGroups; + } + + @Override + public String getDeviceProtectedDataDir() { + return deviceProtectedDataDir; + } + + @Override + public String getCredentialProtectedDataDir() { + return credentialProtectedDataDir; + } + + @Override + public String getSeInfoUser() { + return seInfoUser; + } + + @Override + public String getClassName() { + return className; + } + + @Override + public int getTheme() { + return theme; + } + + @Override + public int getRequiresSmallestWidthDp() { + return requiresSmallestWidthDp; + } + + @Override + public int getCompatibleWidthLimitDp() { + return compatibleWidthLimitDp; + } + + @Override + public int getLargestWidthLimitDp() { + return largestWidthLimitDp; + } + + @Override + public String getScanSourceDir() { + return applicationInfoCodePath; + } + + @Override + public String getScanPublicSourceDir() { + return applicationInfoResourcePath; + } + + @Override + public String getPublicSourceDir() { + return applicationInfoBaseResourcePath; + } + + @Override + public String[] getSplitPublicSourceDirs() { + return applicationInfoSplitResourcePaths; + } + + @Override + public String getSecondaryNativeLibraryDir() { + return secondaryNativeLibraryDir; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public String getManageSpaceActivityName() { + return manageSpaceActivityName; + } + + @Override + public int getDescriptionRes() { + return descriptionRes; + } + + @Override + public String getBackupAgentName() { + return backupAgentName; + } + + @Override + public int getFullBackupContent() { + return fullBackupContent; + } + + @Override + public int getNetworkSecurityConfigRes() { + return networkSecurityConfigRes; + } + + @Override + public int getCategory() { + return category; + } + + @Override + public int getTargetSandboxVersion() { + return targetSandboxVersion; + } + + @Override + public String getAppComponentFactory() { + return appComponentFactory; + } + + @Override + public int getIconRes() { + return iconRes; + } + + @Override + public int getRoundIconRes() { + return roundIconRes; + } + + @Override + public String getZygotePreloadName() { + return zygotePreloadName; + } + + @Override + public int getLabelRes() { + return labelRes; + } + + @Override + public CharSequence getNonLocalizedLabel() { + return nonLocalizedLabel; + } + + @Override + public int getIcon() { + return icon; + } + + @Override + public int getBanner() { + return banner; + } + + @Override + public int getLogo() { + return logo; + } + + @Override + public Bundle getMetaData() { + return appMetaData; + } + + @Override + @Nullable + public List<Intent> getQueriesIntents() { + return queriesIntents; + } + + @Override + @Nullable + public List<String> getQueriesPackages() { + return queriesPackages; + } + + private static void internStringArrayList(List<String> list) { + if (list != null) { + final int N = list.size(); + for (int i = 0; i < N; ++i) { + list.set(i, list.get(i).intern()); + } + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.supportsSmallScreens); + dest.writeInt(this.supportsNormalScreens); + dest.writeInt(this.supportsLargeScreens); + dest.writeInt(this.supportsXLargeScreens); + dest.writeInt(this.resizeable); + dest.writeInt(this.anyDensity); + dest.writeLongArray(this.lastPackageUsageTimeInMills); + dest.writeInt(this.versionCode); + dest.writeInt(this.versionCodeMajor); + dest.writeInt(this.baseRevisionCode); + dest.writeString(this.versionName); + dest.writeBoolean(this.coreApp); + dest.writeInt(this.compileSdkVersion); + dest.writeString(this.compileSdkVersionCodename); + dest.writeString(this.packageName); + dest.writeString(this.realPackage); + dest.writeString(this.manifestPackageName); + dest.writeString(this.baseCodePath); + dest.writeBoolean(this.requiredForAllUsers); + dest.writeString(this.restrictedAccountType); + dest.writeString(this.requiredAccountType); + dest.writeBoolean(this.baseHardwareAccelerated); + dest.writeString(this.overlayTarget); + dest.writeString(this.overlayTargetName); + dest.writeString(this.overlayCategory); + dest.writeInt(this.overlayPriority); + dest.writeBoolean(this.overlayIsStatic); + dest.writeString(this.staticSharedLibName); + dest.writeLong(this.staticSharedLibVersion); + dest.writeStringList(this.libraryNames); + dest.writeStringList(this.usesLibraries); + dest.writeStringList(this.usesOptionalLibraries); + dest.writeStringList(this.usesStaticLibraries); + dest.writeLongArray(this.usesStaticLibrariesVersions); + + if (this.usesStaticLibrariesCertDigests == null) { + dest.writeInt(-1); + } else { + dest.writeInt(this.usesStaticLibrariesCertDigests.length); + for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) { + dest.writeStringArray(this.usesStaticLibrariesCertDigests[index]); + } + } + + dest.writeString(this.sharedUserId); + dest.writeInt(this.sharedUserLabel); + dest.writeTypedList(this.configPreferences); + dest.writeTypedList(this.reqFeatures); + dest.writeTypedList(this.featureGroups); + dest.writeByteArray(this.restrictUpdateHash); + dest.writeStringList(this.originalPackages); + dest.writeStringList(this.adoptPermissions); + dest.writeStringList(this.requestedPermissions); + dest.writeStringList(this.implicitPermissions); + dest.writeArraySet(this.upgradeKeySets); + dest.writeMap(this.keySetMapping); + dest.writeStringList(this.protectedBroadcasts); + dest.writeTypedList(this.activities); + dest.writeTypedList(this.receivers); + dest.writeTypedList(this.services); + dest.writeTypedList(this.providers); + dest.writeTypedList(this.permissions); + dest.writeTypedList(this.permissionGroups); + dest.writeTypedList(this.instrumentations); + ParsedIntentInfo.writeIntentsList(this.preferredActivityFilters, dest, flags); + dest.writeBundle(this.appMetaData); + dest.writeString(this.volumeUuid); + dest.writeString(this.applicationVolumeUuid); + dest.writeParcelable(this.signingDetails, flags); + dest.writeString(this.codePath); + dest.writeBoolean(this.use32BitAbi); + dest.writeBoolean(this.visibleToInstantApps); + dest.writeString(this.cpuAbiOverride); + dest.writeBoolean(this.isStub); + dest.writeInt(this.preferredOrder); + dest.writeBoolean(this.forceQueryable); + dest.writeParcelableList(this.queriesIntents, flags); + dest.writeStringList(this.queriesPackages); + dest.writeString(this.applicationInfoBaseResourcePath); + dest.writeString(this.applicationInfoCodePath); + dest.writeString(this.applicationInfoResourcePath); + dest.writeStringArray(this.applicationInfoSplitResourcePaths); + dest.writeString(this.appComponentFactory); + dest.writeString(this.backupAgentName); + dest.writeInt(this.banner); + dest.writeInt(this.category); + dest.writeString(this.classLoaderName); + dest.writeString(this.className); + dest.writeInt(this.compatibleWidthLimitDp); + dest.writeString(this.credentialProtectedDataDir); + dest.writeString(this.dataDir); + dest.writeInt(this.descriptionRes); + dest.writeString(this.deviceProtectedDataDir); + dest.writeBoolean(this.enabled); + dest.writeInt(this.flags); + dest.writeInt(this.fullBackupContent); + dest.writeBoolean(this.hiddenUntilInstalled); + dest.writeInt(this.icon); + dest.writeInt(this.iconRes); + dest.writeInt(this.installLocation); + dest.writeInt(this.labelRes); + dest.writeInt(this.largestWidthLimitDp); + dest.writeInt(this.logo); + dest.writeString(this.manageSpaceActivityName); + dest.writeFloat(this.maxAspectRatio); + dest.writeFloat(this.minAspectRatio); + dest.writeInt(this.minSdkVersion); + dest.writeString(this.name); + dest.writeString(this.nativeLibraryDir); + dest.writeString(this.nativeLibraryRootDir); + dest.writeBoolean(this.nativeLibraryRootRequiresIsa); + dest.writeInt(this.networkSecurityConfigRes); + dest.writeCharSequence(this.nonLocalizedLabel); + dest.writeString(this.permission); + dest.writeString(this.primaryCpuAbi); + dest.writeInt(this.privateFlags); + dest.writeString(this.processName); + dest.writeInt(this.requiresSmallestWidthDp); + dest.writeInt(this.roundIconRes); + dest.writeString(this.secondaryCpuAbi); + dest.writeString(this.secondaryNativeLibraryDir); + dest.writeString(this.seInfo); + dest.writeString(this.seInfoUser); + dest.writeInt(this.targetSandboxVersion); + dest.writeInt(this.targetSdkVersion); + dest.writeString(this.taskAffinity); + dest.writeInt(this.theme); + dest.writeInt(this.uid); + dest.writeInt(this.uiOptions); + dest.writeStringArray(this.usesLibraryFiles); + dest.writeTypedList(this.usesLibraryInfos); + dest.writeString(this.zygotePreloadName); + dest.writeStringArray(this.splitClassLoaderNames); + dest.writeStringArray(this.splitCodePaths); + dest.writeSparseArray(this.splitDependencies); + dest.writeIntArray(this.splitFlags); + dest.writeStringArray(this.splitNames); + dest.writeIntArray(this.splitRevisionCodes); + } + + public PackageImpl(Parcel in) { + // We use the boot classloader for all classes that we load. + final ClassLoader boot = Object.class.getClassLoader(); + this.supportsSmallScreens = in.readInt(); + this.supportsNormalScreens = in.readInt(); + this.supportsLargeScreens = in.readInt(); + this.supportsXLargeScreens = in.readInt(); + this.resizeable = in.readInt(); + this.anyDensity = in.readInt(); + this.lastPackageUsageTimeInMills = in.createLongArray(); + this.versionCode = in.readInt(); + this.versionCodeMajor = in.readInt(); + this.baseRevisionCode = in.readInt(); + this.versionName = TextUtils.safeIntern(in.readString()); + this.coreApp = in.readBoolean(); + this.compileSdkVersion = in.readInt(); + this.compileSdkVersionCodename = TextUtils.safeIntern(in.readString()); + this.packageName = TextUtils.safeIntern(in.readString()); + this.realPackage = in.readString(); + this.manifestPackageName = in.readString(); + this.baseCodePath = in.readString(); + this.requiredForAllUsers = in.readBoolean(); + this.restrictedAccountType = in.readString(); + this.requiredAccountType = in.readString(); + this.baseHardwareAccelerated = in.readBoolean(); + this.overlayTarget = in.readString(); + this.overlayTargetName = in.readString(); + this.overlayCategory = in.readString(); + this.overlayPriority = in.readInt(); + this.overlayIsStatic = in.readBoolean(); + this.staticSharedLibName = TextUtils.safeIntern(in.readString()); + this.staticSharedLibVersion = in.readLong(); + this.libraryNames = in.createStringArrayList(); + internStringArrayList(this.libraryNames); + this.usesLibraries = in.createStringArrayList(); + internStringArrayList(this.usesLibraries); + this.usesOptionalLibraries = in.createStringArrayList(); + internStringArrayList(this.usesOptionalLibraries); + this.usesStaticLibraries = in.createStringArrayList(); + internStringArrayList(usesStaticLibraries); + this.usesStaticLibrariesVersions = in.createLongArray(); + + int digestsSize = in.readInt(); + if (digestsSize >= 0) { + this.usesStaticLibrariesCertDigests = new String[digestsSize][]; + for (int index = 0; index < digestsSize; index++) { + this.usesStaticLibrariesCertDigests[index] = in.readStringArray(); + } + } + + this.sharedUserId = TextUtils.safeIntern(in.readString()); + this.sharedUserLabel = in.readInt(); + this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR); + this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR); + this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR); + this.restrictUpdateHash = in.createByteArray(); + this.originalPackages = in.createStringArrayList(); + this.adoptPermissions = in.createStringArrayList(); + this.requestedPermissions = in.createStringArrayList(); + internStringArrayList(this.requestedPermissions); + this.implicitPermissions = in.createStringArrayList(); + internStringArrayList(this.implicitPermissions); + this.upgradeKeySets = (ArraySet<String>) in.readArraySet(boot); + this.keySetMapping = in.readHashMap(boot); + this.protectedBroadcasts = in.createStringArrayList(); + internStringArrayList(this.protectedBroadcasts); + this.activities = in.createTypedArrayList(ParsedActivity.CREATOR); + this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR); + this.services = in.createTypedArrayList(ParsedService.CREATOR); + this.providers = in.createTypedArrayList(ParsedProvider.CREATOR); + this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR); + this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR); + this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR); + this.preferredActivityFilters = ParsedIntentInfo.createIntentsList(in); + this.appMetaData = in.readBundle(boot); + this.volumeUuid = in.readString(); + this.applicationVolumeUuid = in.readString(); + this.signingDetails = in.readParcelable(boot); + this.codePath = in.readString(); + this.use32BitAbi = in.readBoolean(); + this.visibleToInstantApps = in.readBoolean(); + this.cpuAbiOverride = in.readString(); + this.isStub = in.readBoolean(); + this.preferredOrder = in.readInt(); + this.forceQueryable = in.readBoolean(); + this.queriesIntents = in.createTypedArrayList(Intent.CREATOR); + this.queriesPackages = in.createStringArrayList(); + internStringArrayList(this.queriesPackages); + this.applicationInfoBaseResourcePath = in.readString(); + this.applicationInfoCodePath = in.readString(); + this.applicationInfoResourcePath = in.readString(); + this.applicationInfoSplitResourcePaths = in.createStringArray(); + this.appComponentFactory = in.readString(); + this.backupAgentName = in.readString(); + this.banner = in.readInt(); + this.category = in.readInt(); + this.classLoaderName = in.readString(); + this.className = in.readString(); + this.compatibleWidthLimitDp = in.readInt(); + this.credentialProtectedDataDir = in.readString(); + this.dataDir = in.readString(); + this.descriptionRes = in.readInt(); + this.deviceProtectedDataDir = in.readString(); + this.enabled = in.readBoolean(); + this.flags = in.readInt(); + this.fullBackupContent = in.readInt(); + this.hiddenUntilInstalled = in.readBoolean(); + this.icon = in.readInt(); + this.iconRes = in.readInt(); + this.installLocation = in.readInt(); + this.labelRes = in.readInt(); + this.largestWidthLimitDp = in.readInt(); + this.logo = in.readInt(); + this.manageSpaceActivityName = in.readString(); + this.maxAspectRatio = in.readFloat(); + this.minAspectRatio = in.readFloat(); + this.minSdkVersion = in.readInt(); + this.name = in.readString(); + this.nativeLibraryDir = in.readString(); + this.nativeLibraryRootDir = in.readString(); + this.nativeLibraryRootRequiresIsa = in.readBoolean(); + this.networkSecurityConfigRes = in.readInt(); + this.nonLocalizedLabel = in.readCharSequence(); + this.permission = TextUtils.safeIntern(in.readString()); + this.primaryCpuAbi = in.readString(); + this.privateFlags = in.readInt(); + this.processName = in.readString(); + this.requiresSmallestWidthDp = in.readInt(); + this.roundIconRes = in.readInt(); + this.secondaryCpuAbi = in.readString(); + this.secondaryNativeLibraryDir = in.readString(); + this.seInfo = in.readString(); + this.seInfoUser = in.readString(); + this.targetSandboxVersion = in.readInt(); + this.targetSdkVersion = in.readInt(); + this.taskAffinity = in.readString(); + this.theme = in.readInt(); + this.uid = in.readInt(); + this.uiOptions = in.readInt(); + this.usesLibraryFiles = in.createStringArray(); + this.usesLibraryInfos = in.createTypedArrayList(SharedLibraryInfo.CREATOR); + this.zygotePreloadName = in.readString(); + this.splitClassLoaderNames = in.createStringArray(); + this.splitCodePaths = in.createStringArray(); + this.splitDependencies = in.readSparseArray(boot); + this.splitFlags = in.createIntArray(); + this.splitNames = in.createStringArray(); + this.splitRevisionCodes = in.createIntArray(); + } + + public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() { + @Override + public PackageImpl createFromParcel(Parcel source) { + return new PackageImpl(source); + } + + @Override + public PackageImpl[] newArray(int size) { + return new PackageImpl[size]; + } + }; +} diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java new file mode 100644 index 000000000000..28f0bc4e431f --- /dev/null +++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import android.annotation.Nullable; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FallbackCategoryProvider; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PackageUserState; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.SELinuxUtil; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; +import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; + +import com.android.internal.util.ArrayUtils; + +import java.util.Set; + +/** @hide */ +public class PackageInfoUtils { + + private static final String TAG = ApkParseUtils.TAG; + + /** + * Returns true if the package is installed and not hidden, or if the caller + * explicitly wanted all uninstalled and hidden packages as well. + */ + private static boolean checkUseInstalledOrHidden(AndroidPackage pkg, PackageUserState state, + @PackageManager.PackageInfoFlags int flags) { + // Returns false if the package is hidden system app until installed. + if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0 + && !state.installed + && pkg.isHiddenUntilInstalled()) { + return false; + } + + // If available for the target user, or trying to match uninstalled packages and it's + // a system app. + return state.isAvailable(flags) + || (pkg.isSystemApp() + && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0 + || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0)); + } + + public static PackageInfo generate(AndroidPackage pkg, int[] gids, + @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, + Set<String> grantedPermissions, PackageUserState state, int userId) { + if (!checkUseInstalledOrHidden(pkg, state, flags) || !pkg.isMatch(flags)) { + return null; + } + ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId); + + PackageInfo pi = new PackageInfo(); + pi.packageName = pkg.getPackageName(); + pi.splitNames = pkg.getSplitNames(); + pi.versionCode = pkg.getVersionCode(); + pi.versionCodeMajor = pkg.getVersionCodeMajor(); + pi.baseRevisionCode = pkg.getBaseRevisionCode(); + pi.splitRevisionCodes = pkg.getSplitRevisionCodes(); + pi.versionName = pkg.getVersionName(); + pi.sharedUserId = pkg.getSharedUserId(); + pi.sharedUserLabel = pkg.getSharedUserLabel(); + pi.applicationInfo = applicationInfo; + pi.installLocation = pkg.getInstallLocation(); + pi.isStub = pkg.isStub(); + pi.coreApp = pkg.isCoreApp(); + if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 + || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + pi.requiredForAllUsers = pkg.isRequiredForAllUsers(); + } + pi.restrictedAccountType = pkg.getRestrictedAccountType(); + pi.requiredAccountType = pkg.getRequiredAccountType(); + pi.overlayTarget = pkg.getOverlayTarget(); + pi.targetOverlayableName = pkg.getOverlayTargetName(); + pi.overlayCategory = pkg.getOverlayCategory(); + pi.overlayPriority = pkg.getOverlayPriority(); + pi.mOverlayIsStatic = pkg.isOverlayIsStatic(); + pi.compileSdkVersion = pkg.getCompileSdkVersion(); + pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName(); + pi.firstInstallTime = firstInstallTime; + pi.lastUpdateTime = lastUpdateTime; + if ((flags & PackageManager.GET_GIDS) != 0) { + pi.gids = gids; + } + if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) { + int size = pkg.getConfigPreferences() != null ? pkg.getConfigPreferences().size() : 0; + if (size > 0) { + pi.configPreferences = new ConfigurationInfo[size]; + pkg.getConfigPreferences().toArray(pi.configPreferences); + } + size = pkg.getReqFeatures() != null ? pkg.getReqFeatures().size() : 0; + if (size > 0) { + pi.reqFeatures = new FeatureInfo[size]; + pkg.getReqFeatures().toArray(pi.reqFeatures); + } + size = pkg.getFeatureGroups() != null ? pkg.getFeatureGroups().size() : 0; + if (size > 0) { + pi.featureGroups = new FeatureGroupInfo[size]; + pkg.getFeatureGroups().toArray(pi.featureGroups); + } + } + if ((flags & PackageManager.GET_ACTIVITIES) != 0) { + if (pkg.getActivities() != null) { + final int N = pkg.getActivities().size(); + if (N > 0) { + int num = 0; + final ActivityInfo[] res = new ActivityInfo[N]; + for (int i = 0; i < N; i++) { + final ParsedActivity a = pkg.getActivities().get(i); + if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), a, flags)) { + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals( + a.className)) { + continue; + } + res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo, + userId); + } + } + pi.activities = ArrayUtils.trimToSize(res, num); + } + } + } + if ((flags & PackageManager.GET_RECEIVERS) != 0) { + if (pkg.getReceivers() != null) { + final int size = pkg.getReceivers().size(); + if (size > 0) { + int num = 0; + final ActivityInfo[] res = new ActivityInfo[size]; + for (int i = 0; i < size; i++) { + final ParsedActivity a = pkg.getReceivers().get(i); + if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), a, flags)) { + res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo, + userId); + } + } + pi.receivers = ArrayUtils.trimToSize(res, num); + } + } + } + if ((flags & PackageManager.GET_SERVICES) != 0) { + if (pkg.getServices() != null) { + final int size = pkg.getServices().size(); + if (size > 0) { + int num = 0; + final ServiceInfo[] res = new ServiceInfo[size]; + for (int i = 0; i < size; i++) { + final ComponentParseUtils.ParsedService s = pkg.getServices().get(i); + if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), s, flags)) { + res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo, + userId); + } + } + pi.services = ArrayUtils.trimToSize(res, num); + } + } + } + if ((flags & PackageManager.GET_PROVIDERS) != 0) { + if (pkg.getProviders() != null) { + final int size = pkg.getProviders().size(); + if (size > 0) { + int num = 0; + final ProviderInfo[] res = new ProviderInfo[size]; + for (int i = 0; i < size; i++) { + final ComponentParseUtils.ParsedProvider pr = pkg.getProviders() + .get(i); + if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), pr, flags)) { + res[num++] = generateProviderInfo(pkg, pr, flags, state, + applicationInfo, userId); + } + } + pi.providers = ArrayUtils.trimToSize(res, num); + } + } + } + if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) { + if (pkg.getInstrumentations() != null) { + int N = pkg.getInstrumentations().size(); + if (N > 0) { + pi.instrumentation = new InstrumentationInfo[N]; + for (int i = 0; i < N; i++) { + pi.instrumentation[i] = generateInstrumentationInfo( + pkg.getInstrumentations().get(i), pkg, flags); + } + } + } + } + if ((flags & PackageManager.GET_PERMISSIONS) != 0) { + if (pkg.getPermissions() != null) { + int N = ArrayUtils.size(pkg.getPermissions()); + if (N > 0) { + pi.permissions = new PermissionInfo[N]; + for (int i = 0; i < N; i++) { + pi.permissions[i] = generatePermissionInfo( + pkg.getPermissions().get(i), + flags + ); + } + } + } + if (pkg.getRequestedPermissions() != null) { + int N = pkg.getRequestedPermissions().size(); + if (N > 0) { + pi.requestedPermissions = new String[N]; + pi.requestedPermissionsFlags = new int[N]; + for (int i = 0; i < N; i++) { + final String perm = pkg.getRequestedPermissions().get(i); + pi.requestedPermissions[i] = perm; + // The notion of required permissions is deprecated but for compatibility. + pi.requestedPermissionsFlags[i] |= + PackageInfo.REQUESTED_PERMISSION_REQUIRED; + if (grantedPermissions != null && grantedPermissions.contains(perm)) { + pi.requestedPermissionsFlags[i] |= + PackageInfo.REQUESTED_PERMISSION_GRANTED; + } + } + } + } + } + + PackageParser.SigningDetails signingDetails = pkg.getSigningDetails(); + // deprecated method of getting signing certificates + if ((flags & PackageManager.GET_SIGNATURES) != 0) { + if (signingDetails.hasPastSigningCertificates()) { + // Package has included signing certificate rotation information. Return the oldest + // cert so that programmatic checks keep working even if unaware of key rotation. + pi.signatures = new Signature[1]; + pi.signatures[0] = signingDetails.pastSigningCertificates[0]; + } else if (signingDetails.hasSignatures()) { + // otherwise keep old behavior + int numberOfSigs = signingDetails.signatures.length; + pi.signatures = new Signature[numberOfSigs]; + System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0, + numberOfSigs); + } + } + + // replacement for GET_SIGNATURES + if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) { + if (signingDetails != PackageParser.SigningDetails.UNKNOWN) { + // only return a valid SigningInfo if there is signing information to report + pi.signingInfo = new SigningInfo(signingDetails); + } else { + pi.signingInfo = null; + } + } + + return pi; + } + + @Nullable + public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg, + @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) { + + if (pkg == null) return null; + if (!checkUseInstalledOrHidden(pkg, state, flags) || !pkg.isMatch(flags)) { + return null; + } + + // Make shallow copy so we can store the metadata/libraries safely + ApplicationInfo ai = pkg.toAppInfoWithoutState(); + ai.initForUser(userId); + if ((flags & PackageManager.GET_META_DATA) == 0) { + ai.metaData = null; + } + if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) { + ai.sharedLibraryFiles = null; + ai.sharedLibraryInfos = null; + } + if (state.stopped) { + ai.flags |= ApplicationInfo.FLAG_STOPPED; + } else { + ai.flags &= ~ApplicationInfo.FLAG_STOPPED; + } + updateApplicationInfo(ai, flags, state); + + return ai; + } + + private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a, + @PackageManager.ComponentInfoFlags int flags, PackageUserState state, + @Nullable ApplicationInfo applicationInfo, int userId) { + if (a == null) return null; + if (!checkUseInstalledOrHidden(pkg, state, flags)) { + return null; + } + if (applicationInfo == null) { + applicationInfo = generateApplicationInfo(pkg, flags, state, userId); + } + // Make shallow copies so we can store the metadata safely + ActivityInfo ai = new ActivityInfo(); + assignSharedFieldsForComponentInfo(ai, a); + ai.targetActivity = a.targetActivity; + ai.processName = a.getProcessName(); + ai.exported = a.exported; + ai.theme = a.theme; + ai.uiOptions = a.uiOptions; + ai.parentActivityName = a.parentActivityName; + ai.permission = a.getPermission(); + ai.taskAffinity = a.taskAffinity; + ai.flags = a.flags; + ai.privateFlags = a.privateFlags; + ai.launchMode = a.launchMode; + ai.documentLaunchMode = a.documentLaunchMode; + ai.maxRecents = a.maxRecents; + ai.configChanges = a.configChanges; + ai.softInputMode = a.softInputMode; + ai.persistableMode = a.persistableMode; + ai.lockTaskLaunchMode = a.lockTaskLaunchMode; + ai.screenOrientation = a.screenOrientation; + ai.resizeMode = a.resizeMode; + ai.maxAspectRatio = a.maxAspectRatio; + ai.minAspectRatio = a.minAspectRatio; + ai.requestedVrComponent = a.requestedVrComponent; + ai.rotationAnimation = a.rotationAnimation; + ai.colorMode = a.colorMode; + ai.windowLayout = a.windowLayout; + ai.metaData = a.metaData; + ai.applicationInfo = applicationInfo; + return ai; + } + + public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a, + @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) { + return generateActivityInfo(pkg, a, flags, state, null, userId); + } + + private static ServiceInfo generateServiceInfo(AndroidPackage pkg, + ComponentParseUtils.ParsedService s, @PackageManager.ComponentInfoFlags int flags, + PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId) { + if (s == null) return null; + if (!checkUseInstalledOrHidden(pkg, state, flags)) { + return null; + } + if (applicationInfo == null) { + applicationInfo = generateApplicationInfo(pkg, flags, state, userId); + } + // Make shallow copies so we can store the metadata safely + ServiceInfo si = new ServiceInfo(); + assignSharedFieldsForComponentInfo(si, s); + si.exported = s.exported; + si.flags = s.flags; + si.metaData = s.metaData; + si.permission = s.getPermission(); + si.processName = s.getProcessName(); + si.mForegroundServiceType = s.foregroundServiceType; + si.metaData = s.metaData; + si.applicationInfo = applicationInfo; + return si; + } + + public static ServiceInfo generateServiceInfo(AndroidPackage pkg, + ComponentParseUtils.ParsedService s, @PackageManager.ComponentInfoFlags int flags, + PackageUserState state, int userId) { + return generateServiceInfo(pkg, s, flags, state, null, userId); + } + + private static ProviderInfo generateProviderInfo(AndroidPackage pkg, + ComponentParseUtils.ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, + PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId) { + if (p == null) return null; + if (!checkUseInstalledOrHidden(pkg, state, flags)) { + return null; + } + if (applicationInfo == null) { + applicationInfo = generateApplicationInfo(pkg, flags, state, userId); + } + // Make shallow copies so we can store the metadata safely + ProviderInfo pi = new ProviderInfo(); + assignSharedFieldsForComponentInfo(pi, p); + pi.exported = p.exported; + pi.flags = p.flags; + pi.processName = p.getProcessName(); + pi.authority = p.getAuthority(); + pi.isSyncable = p.isSyncable; + pi.readPermission = p.getReadPermission(); + pi.writePermission = p.getWritePermission(); + pi.grantUriPermissions = p.grantUriPermissions; + pi.forceUriPermissions = p.forceUriPermissions; + pi.multiprocess = p.multiProcess; + pi.initOrder = p.initOrder; + pi.uriPermissionPatterns = p.uriPermissionPatterns; + pi.pathPermissions = p.pathPermissions; + pi.metaData = p.metaData; + if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) { + pi.uriPermissionPatterns = null; + } + pi.applicationInfo = applicationInfo; + return pi; + } + + public static ProviderInfo generateProviderInfo(AndroidPackage pkg, + ComponentParseUtils.ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, + PackageUserState state, int userId) { + return generateProviderInfo(pkg, p, flags, state, null, userId); + } + + public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i, + AndroidPackage pkg, @PackageManager.ComponentInfoFlags int flags) { + if (i == null) return null; + + InstrumentationInfo ii = new InstrumentationInfo(); + assignSharedFieldsForPackageItemInfo(ii, i); + ii.targetPackage = i.getTargetPackage(); + ii.targetProcesses = i.getTargetProcesses(); + ii.handleProfiling = i.handleProfiling; + ii.functionalTest = i.functionalTest; + + ii.sourceDir = pkg.getBaseCodePath(); + ii.publicSourceDir = pkg.getCodePath(); + ii.splitNames = pkg.getSplitNames(); + ii.splitSourceDirs = pkg.getSplitCodePaths(); + ii.splitPublicSourceDirs = pkg.getSplitCodePaths(); + ii.splitDependencies = pkg.getSplitDependencies(); + ii.dataDir = pkg.getDataDir(); + ii.deviceProtectedDataDir = pkg.getDeviceProtectedDataDir(); + ii.credentialProtectedDataDir = pkg.getCredentialProtectedDataDir(); + ii.primaryCpuAbi = pkg.getPrimaryCpuAbi(); + ii.secondaryCpuAbi = pkg.getSecondaryCpuAbi(); + ii.nativeLibraryDir = pkg.getNativeLibraryDir(); + ii.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir(); + + if ((flags & PackageManager.GET_META_DATA) == 0) { + return ii; + } + ii.metaData = i.metaData; + return ii; + } + + public static PermissionInfo generatePermissionInfo(ParsedPermission p, + @PackageManager.ComponentInfoFlags int flags) { + if (p == null) return null; + + PermissionInfo pi = new PermissionInfo(p.backgroundPermission); + assignSharedFieldsForPackageItemInfo(pi, p); + pi.group = p.getGroup(); + pi.requestRes = p.requestRes; + pi.protectionLevel = p.protectionLevel; + pi.descriptionRes = p.descriptionRes; + pi.flags = p.flags; + + if ((flags & PackageManager.GET_META_DATA) == 0) { + return pi; + } + pi.metaData = p.metaData; + return pi; + } + + public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg, + @PackageManager.ComponentInfoFlags int flags) { + if (pg == null) return null; + + PermissionGroupInfo pgi = new PermissionGroupInfo( + pg.requestDetailResourceId, + pg.backgroundRequestResourceId, + pg.backgroundRequestDetailResourceId + ); + assignSharedFieldsForPackageItemInfo(pgi, pg); + pgi.priority = pg.priority; + pgi.requestRes = pg.requestRes; + pgi.flags = pg.flags; + + if ((flags & PackageManager.GET_META_DATA) == 0) { + return pgi; + } + pgi.metaData = pg.metaData; + return pgi; + } + + private static void updateApplicationInfo(ApplicationInfo ai, + @PackageManager.ApplicationInfoFlags int flags, + PackageUserState state) { + // CompatibilityMode is global state. + if (!PackageParser.sCompatibilityModeEnabled) { + ai.disableCompatibilityMode(); + } + if (state.installed) { + ai.flags |= ApplicationInfo.FLAG_INSTALLED; + } else { + ai.flags &= ~ApplicationInfo.FLAG_INSTALLED; + } + if (state.suspended) { + ai.flags |= ApplicationInfo.FLAG_SUSPENDED; + } else { + ai.flags &= ~ApplicationInfo.FLAG_SUSPENDED; + } + if (state.instantApp) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_INSTANT; + } else { + ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT; + } + if (state.virtualPreload) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD; + } else { + ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD; + } + if (state.hidden) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN; + } else { + ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN; + } + if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + ai.enabled = true; + } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { + ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0; + } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED + || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { + ai.enabled = false; + } + ai.enabledSetting = state.enabled; + if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { + ai.category = state.categoryHint; + } + if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { + ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); + } + ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); + ai.resourceDirs = state.overlayPaths; + ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0) + ? ai.roundIconRes : ai.iconRes; + } + + private static void assignSharedFieldsForPackageItemInfo(PackageItemInfo packageItemInfo, + ComponentParseUtils.ParsedComponent parsedComponent) { + packageItemInfo.banner = parsedComponent.banner; + packageItemInfo.icon = parsedComponent.icon; + packageItemInfo.labelRes = parsedComponent.labelRes; + packageItemInfo.logo = parsedComponent.logo; + packageItemInfo.name = parsedComponent.className; + packageItemInfo.nonLocalizedLabel = parsedComponent.nonLocalizedLabel; + packageItemInfo.packageName = parsedComponent.getPackageName(); + } + + private static void assignSharedFieldsForComponentInfo(ComponentInfo componentInfo, + ComponentParseUtils.ParsedComponent parsedComponent) { + assignSharedFieldsForPackageItemInfo(componentInfo, parsedComponent); + componentInfo.descriptionRes = parsedComponent.descriptionRes; + componentInfo.directBootAware = parsedComponent.directBootAware; + componentInfo.enabled = parsedComponent.enabled; + componentInfo.splitName = parsedComponent.getSplitName(); + } + +} diff --git a/core/java/android/content/pm/parsing/ParsedPackage.java b/core/java/android/content/pm/parsing/ParsedPackage.java new file mode 100644 index 000000000000..05cf586522f2 --- /dev/null +++ b/core/java/android/content/pm/parsing/ParsedPackage.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import android.content.pm.PackageParser; + +/** + * Methods used for mutation after direct package parsing, mostly done inside + * {@link com.android.server.pm.PackageManagerService}. + * + * Java disallows defining this as an inner interface, so this must be a separate file. + * + * @hide + */ +public interface ParsedPackage extends AndroidPackage { + + AndroidPackage hideAsFinal(); + + ParsedPackage addUsesLibrary(int index, String libraryName); + + ParsedPackage addUsesOptionalLibrary(int index, String libraryName); + + ParsedPackage capPermissionPriorities(); + + ParsedPackage clearAdoptPermissions(); + + ParsedPackage clearOriginalPackages(); + + ParsedPackage clearProtectedBroadcasts(); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #setCodePath(String)} + */ + @Deprecated + ParsedPackage setApplicationInfoCodePath(String applicationInfoCodePath); + + /** + * TODO(b/135203078): Use non-AppInfo method + * @deprecated use {@link #setCodePath(String)} + */ + @Deprecated + ParsedPackage setApplicationInfoResourcePath(String applicationInfoResourcePath); + + ParsedPackage setBaseCodePath(String baseCodePath); + + ParsedPackage setCodePath(String codePath); + + ParsedPackage setCpuAbiOverride(String cpuAbiOverride); + + ParsedPackage setNativeLibraryDir(String nativeLibraryDir); + + ParsedPackage setNativeLibraryRootDir(String nativeLibraryRootDir); + + ParsedPackage setPackageName(String packageName); + + ParsedPackage setPrimaryCpuAbi(String primaryCpuAbi); + + ParsedPackage setProcessName(String processName); + + ParsedPackage setRealPackage(String realPackage); + + ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi); + + ParsedPackage setSigningDetails(PackageParser.SigningDetails signingDetails); + + ParsedPackage setSplitCodePaths(String[] splitCodePaths); + + ParsedPackage initForUser(int userId); + + ParsedPackage setNativeLibraryRootRequiresIsa(boolean nativeLibraryRootRequiresIsa); + + ParsedPackage setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware); + + ParsedPackage setFactoryTest(boolean factoryTest); + + ParsedPackage markNotActivitiesAsNotExportedIfSingleUser(); + + ParsedPackage setOdm(boolean odm); + + ParsedPackage setOem(boolean oem); + + ParsedPackage setPrivileged(boolean privileged); + + ParsedPackage setProduct(boolean product); + + ParsedPackage setSignedWithPlatformKey(boolean signedWithPlatformKey); + + ParsedPackage setSystem(boolean system); + + ParsedPackage setSystemExt(boolean systemExt); + + ParsedPackage setUpdatedSystemApp(boolean updatedSystemApp); + + ParsedPackage setVendor(boolean vendor); + + ParsedPackage removePermission(int index); + + ParsedPackage removeUsesLibrary(String libraryName); + + ParsedPackage removeUsesOptionalLibrary(String libraryName); + + ParsedPackage setApplicationInfoBaseResourcePath(String applicationInfoBaseResourcePath); + + ParsedPackage setApplicationInfoSplitResourcePaths( + String[] applicationInfoSplitResourcePaths); + + ParsedPackage setApplicationVolumeUuid(String applicationVolumeUuid); + + ParsedPackage setCoreApp(boolean coreApp); + + ParsedPackage setIsStub(boolean isStub); + + // TODO(b/135203078): Remove entirely + ParsedPackage setPackageSettingCallback(PackageSettingCallback packageSettingCallback); + + ParsedPackage setRestrictUpdateHash(byte[] restrictUpdateHash); + + ParsedPackage setSeInfo(String seInfo); + + ParsedPackage setSeInfoUser(String seInfoUser); + + ParsedPackage setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir); + + ParsedPackage setUid(int uid); + + ParsedPackage setVersionCode(int versionCode); + + ParsedPackage setVersionCodeMajor(int versionCodeMajor); + + // TODO(b/135203078): Move logic earlier in parse chain so nothing needs to be reverted + ParsedPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage); + + ParsedPackage setDirectBootAware(boolean directBootAware); + + ParsedPackage setPersistent(boolean persistent); + + interface PackageSettingCallback { + default void setAndroidPackage(AndroidPackage pkg){} + } +} diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java new file mode 100644 index 000000000000..43c1f6e335b0 --- /dev/null +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2019 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 android.content.pm.parsing; + +import android.annotation.Nullable; +import android.content.Intent; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageParser; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; +import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo; +import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; +import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; +import android.content.pm.parsing.ComponentParseUtils.ParsedProvider; +import android.content.pm.parsing.ComponentParseUtils.ParsedService; +import android.os.Bundle; +import android.util.ArraySet; +import android.util.SparseArray; + +import java.security.PublicKey; + +/** + * Methods used for mutation during direct package parsing. + * + * Java disallows defining this as an inner interface, so this must be a separate file. + * + * @hide + */ +public interface ParsingPackage extends AndroidPackage { + + ParsingPackage addActivity(ParsedActivity parsedActivity); + + ParsingPackage addAdoptPermission(String adoptPermission); + + ParsingPackage addConfigPreference(ConfigurationInfo configPreference); + + ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup); + + ParsingPackage addImplicitPermission(String permission); + + ParsingPackage addInstrumentation(ParsedInstrumentation instrumentation); + + ParsingPackage addKeySet(String keySetName, PublicKey publicKey); + + ParsingPackage addLibraryName(String libraryName); + + ParsingPackage addOriginalPackage(String originalPackage); + + ParsingPackage addPermission(ParsedPermission permission); + + ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup); + + ParsingPackage addPreferredActivityFilter(ParsedActivityIntentInfo activityIntentInfo); + + ParsingPackage addProtectedBroadcast(String protectedBroadcast); + + ParsingPackage addProvider(ParsedProvider parsedProvider); + + ParsingPackage addReceiver(ParsedActivity parsedReceiver); + + ParsingPackage addReqFeature(FeatureInfo reqFeature); + + ParsingPackage addRequestedPermission(String permission); + + ParsingPackage addService(ParsedService parsedService); + + ParsingPackage addUsesLibrary(String libraryName); + + ParsingPackage addUsesOptionalLibrary(String libraryName); + + ParsingPackage addUsesStaticLibrary(String libraryName); + + ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests); + + ParsingPackage addUsesStaticLibraryVersion(long version); + + ParsingPackage addQueriesIntent(Intent intent); + + ParsingPackage addQueriesPackage(String packageName); + + ParsingPackage asSplit( + String[] splitNames, + String[] splitCodePaths, + int[] splitRevisionCodes, + @Nullable SparseArray<int[]> splitDependencies + ); + + ParsingPackage setAppMetaData(Bundle appMetaData); + + ParsingPackage setForceQueryable(boolean forceQueryable); + + ParsingPackage setMaxAspectRatio(float maxAspectRatio); + + ParsingPackage setMinAspectRatio(float minAspectRatio); + + ParsingPackage setName(String name); + + ParsingPackage setPermission(String permission); + + ParsingPackage setProcessName(String processName); + + ParsingPackage setSharedUserId(String sharedUserId); + + ParsingPackage setStaticSharedLibName(String staticSharedLibName); + + ParsingPackage setTaskAffinity(String taskAffinity); + + ParsingPackage setTargetSdkVersion(int targetSdkVersion); + + ParsingPackage setUiOptions(int uiOptions); + + ParsingPackage setBaseHardwareAccelerated(boolean baseHardwareAccelerated); + + ParsingPackage setActivitiesResizeModeResizeable(boolean resizeable); + + ParsingPackage setActivitiesResizeModeResizeableViaSdkVersion(boolean resizeableViaSdkVersion); + + ParsingPackage setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture); + + ParsingPackage setAllowBackup(boolean allowBackup); + + ParsingPackage setAllowClearUserData(boolean allowClearUserData); + + ParsingPackage setAllowClearUserDataOnFailedRestore(boolean allowClearUserDataOnFailedRestore); + + ParsingPackage setAllowTaskReparenting(boolean allowTaskReparenting); + + ParsingPackage setIsOverlay(boolean isOverlay); + + ParsingPackage setBackupInForeground(boolean backupInForeground); + + ParsingPackage setCantSaveState(boolean cantSaveState); + + ParsingPackage setDebuggable(boolean debuggable); + + ParsingPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage); + + ParsingPackage setDirectBootAware(boolean directBootAware); + + ParsingPackage setExternalStorage(boolean externalStorage); + + ParsingPackage setExtractNativeLibs(boolean extractNativeLibs); + + ParsingPackage setFullBackupOnly(boolean fullBackupOnly); + + ParsingPackage setHasCode(boolean hasCode); + + ParsingPackage setHasFragileUserData(boolean hasFragileUserData); + + ParsingPackage setIsGame(boolean isGame); + + ParsingPackage setIsolatedSplitLoading(boolean isolatedSplitLoading); + + ParsingPackage setKillAfterRestore(boolean killAfterRestore); + + ParsingPackage setLargeHeap(boolean largeHeap); + + ParsingPackage setMultiArch(boolean multiArch); + + ParsingPackage setPartiallyDirectBootAware(boolean partiallyDirectBootAware); + + ParsingPackage setPersistent(boolean persistent); + + ParsingPackage setProfileableByShell(boolean profileableByShell); + + ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage); + + ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion); + + ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode); + + ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary); + + ParsingPackage setSupportsRtl(boolean supportsRtl); + + ParsingPackage setTestOnly(boolean testOnly); + + ParsingPackage setUseEmbeddedDex(boolean useEmbeddedDex); + + ParsingPackage setUsesCleartextTraffic(boolean usesCleartextTraffic); + + ParsingPackage setUsesNonSdkApi(boolean usesNonSdkApi); + + ParsingPackage setVisibleToInstantApps(boolean visibleToInstantApps); + + ParsingPackage setVmSafeMode(boolean vmSafeMode); + + ParsingPackage removeUsesOptionalLibrary(String libraryName); + + ParsingPackage setAnyDensity(int anyDensity); + + ParsingPackage setAppComponentFactory(String appComponentFactory); + + ParsingPackage setApplicationVolumeUuid(String applicationVolumeUuid); + + ParsingPackage setBackupAgentName(String backupAgentName); + + ParsingPackage setBanner(int banner); + + ParsingPackage setCategory(int category); + + ParsingPackage setClassLoaderName(String classLoaderName); + + ParsingPackage setClassName(String className); + + ParsingPackage setCodePath(String codePath); + + ParsingPackage setCompatibleWidthLimitDp(int compatibleWidthLimitDp); + + ParsingPackage setDescriptionRes(int descriptionRes); + + ParsingPackage setEnabled(boolean enabled); + + ParsingPackage setFullBackupContent(int fullBackupContent); + + ParsingPackage setHasDomainUrls(boolean hasDomainUrls); + + ParsingPackage setIcon(int icon); + + ParsingPackage setIconRes(int iconRes); + + ParsingPackage setInstallLocation(int installLocation); + + ParsingPackage setLabelRes(int labelRes); + + ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp); + + ParsingPackage setLogo(int logo); + + ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName); + + ParsingPackage setMinSdkVersion(int minSdkVersion); + + ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes); + + ParsingPackage setNonLocalizedLabel(CharSequence nonLocalizedLabel); + + ParsingPackage setOverlayCategory(String overlayCategory); + + ParsingPackage setOverlayIsStatic(boolean overlayIsStatic); + + ParsingPackage setOverlayPriority(int overlayPriority); + + ParsingPackage setOverlayTarget(String overlayTarget); + + ParsingPackage setOverlayTargetName(String overlayTargetName); + + ParsingPackage setRealPackage(String realPackage); + + ParsingPackage setRequiredAccountType(String requiredAccountType); + + ParsingPackage setRequiredForAllUsers(boolean requiredForAllUsers); + + ParsingPackage setRequiresSmallestWidthDp(int requiresSmallestWidthDp); + + ParsingPackage setResizeable(int resizeable); + + ParsingPackage setRestrictUpdateHash(byte[] restrictUpdateHash); + + ParsingPackage setRestrictedAccountType(String restrictedAccountType); + + ParsingPackage setRoundIconRes(int roundIconRes); + + ParsingPackage setSharedUserLabel(int sharedUserLabel); + + ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails); + + ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName); + + ParsingPackage setStaticSharedLibVersion(long staticSharedLibVersion); + + ParsingPackage setSupportsLargeScreens(int supportsLargeScreens); + + ParsingPackage setSupportsNormalScreens(int supportsNormalScreens); + + ParsingPackage setSupportsSmallScreens(int supportsSmallScreens); + + ParsingPackage setSupportsXLargeScreens(int supportsXLargeScreens); + + ParsingPackage setTargetSandboxVersion(int targetSandboxVersion); + + ParsingPackage setTheme(int theme); + + ParsingPackage setUpgradeKeySets(ArraySet<String> upgradeKeySets); + + ParsingPackage setUse32BitAbi(boolean use32BitAbi); + + ParsingPackage setVolumeUuid(String volumeUuid); + + ParsingPackage setZygotePreloadName(String zygotePreloadName); + + ParsingPackage sortActivities(); + + ParsingPackage sortReceivers(); + + ParsingPackage sortServices(); + + ParsedPackage hideAsParsed(); + + ParsingPackage setBaseRevisionCode(int baseRevisionCode); + + ParsingPackage setPreferredOrder(int preferredOrder); + + ParsingPackage setVersionName(String versionName); + + ParsingPackage setCompileSdkVersion(int compileSdkVersion); + + ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename); + + boolean usesCompatibilityMode(); +} diff --git a/core/java/android/content/pm/AndroidHidlUpdater.java b/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java index d0657e5eb8ec..81b4bc574197 100644 --- a/core/java/android/content/pm/AndroidHidlUpdater.java +++ b/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.content.pm; +package android.content.pm.parsing.library; -import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE; -import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER; -import android.content.pm.PackageParser.Package; +import android.content.pm.parsing.ParsedPackage; import android.os.Build; import com.android.internal.annotations.VisibleForTesting; @@ -33,20 +33,18 @@ import com.android.internal.annotations.VisibleForTesting; public class AndroidHidlUpdater extends PackageSharedLibraryUpdater { @Override - public void updatePackage(Package pkg) { - ApplicationInfo info = pkg.applicationInfo; - + public void updatePackage(ParsedPackage parsedPackage) { // This was the default <= P and is maintained for backwards compatibility. - boolean isLegacy = info.targetSdkVersion <= Build.VERSION_CODES.P; + boolean isLegacy = parsedPackage.getTargetSdkVersion() <= Build.VERSION_CODES.P; // Only system apps use these libraries - boolean isSystem = info.isSystemApp() || info.isUpdatedSystemApp(); + boolean isSystem = parsedPackage.isSystemApp() || parsedPackage.isUpdatedSystemApp(); if (isLegacy && isSystem) { - prefixRequiredLibrary(pkg, ANDROID_HIDL_BASE); - prefixRequiredLibrary(pkg, ANDROID_HIDL_MANAGER); + prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_BASE); + prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_MANAGER); } else { - removeLibrary(pkg, ANDROID_HIDL_BASE); - removeLibrary(pkg, ANDROID_HIDL_MANAGER); + removeLibrary(parsedPackage, ANDROID_HIDL_BASE); + removeLibrary(parsedPackage, ANDROID_HIDL_MANAGER); } } } diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java index 18d3ba33552e..5fbe5b9c7250 100644 --- a/core/java/android/content/pm/AndroidTestBaseUpdater.java +++ b/core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.content.pm; +package android.content.pm.parsing.library; -import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE; -import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.Context; -import android.content.pm.PackageParser.Package; +import android.content.pm.parsing.AndroidPackage; +import android.content.pm.parsing.ParsedPackage; import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; @@ -54,25 +55,25 @@ public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long REMOVE_ANDROID_TEST_BASE = 133396946L; - private static boolean isChangeEnabled(Package pkg) { + private static boolean isChangeEnabled(AndroidPackage pkg) { // Do not ask platform compat for system apps to prevent a boot time regression in tests. // b/142558883. - if (!pkg.applicationInfo.isSystemApp()) { + if (!pkg.isSystem()) { IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); try { return platformCompat.isChangeEnabled(REMOVE_ANDROID_TEST_BASE, - pkg.applicationInfo); + pkg.toAppInfoWithoutState()); } catch (RemoteException | NullPointerException e) { Log.e(TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); } } // Fall back to previous behaviour. - return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.Q; + return pkg.getTargetSdkVersion() > Build.VERSION_CODES.Q; } @Override - public void updatePackage(Package pkg) { + public void updatePackage(ParsedPackage pkg) { // Packages targeted at <= Q expect the classes in the android.test.base library // to be accessible so this maintains backward compatibility by adding the // android.test.base library to those packages. diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java index 707443b19679..613a06b636e9 100644 --- a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java +++ b/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.content.pm; +package android.content.pm.parsing.library; -import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; +import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; -import android.content.pm.PackageParser.Package; +import android.content.pm.parsing.AndroidPackage; +import android.content.pm.parsing.ParsedPackage; import android.os.Build; import com.android.internal.annotations.VisibleForTesting; @@ -31,18 +32,17 @@ import com.android.internal.annotations.VisibleForTesting; @VisibleForTesting public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater { - private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) { - int targetSdkVersion = pkg.applicationInfo.targetSdkVersion; - return targetSdkVersion < Build.VERSION_CODES.P; + private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(AndroidPackage pkg) { + return pkg.getTargetSdkVersion() < Build.VERSION_CODES.P; } @Override - public void updatePackage(Package pkg) { + public void updatePackage(ParsedPackage parsedPackage) { // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library // to be accessible so this maintains backward compatibility by adding the // org.apache.http.legacy library to those packages. - if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) { - prefixRequiredLibrary(pkg, ORG_APACHE_HTTP_LEGACY); + if (apkTargetsApiLevelLessThanOrEqualToOMR1(parsedPackage)) { + prefixRequiredLibrary(parsedPackage, ORG_APACHE_HTTP_LEGACY); } } } diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java index 4331bd4ac4d4..1220fc497b04 100644 --- a/core/java/android/content/pm/PackageBackwardCompatibility.java +++ b/core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,14 +14,14 @@ * limitations under the License. */ -package android.content.pm; +package android.content.pm.parsing.library; -import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE; -import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK; -import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER; -import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK; +import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; +import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; -import android.content.pm.PackageParser.Package; +import android.content.pm.parsing.ParsedPackage; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -31,7 +31,7 @@ import java.util.List; import java.util.function.Supplier; /** - * Modifies {@link Package} in order to maintain backwards compatibility. + * Modifies {@link ParsedPackage} in order to maintain backwards compatibility. * * @hide */ @@ -60,7 +60,7 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { // will remove any references to org.apache.http.library from the package so that it does // not try and load the library when it is on the bootclasspath. boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters, - "android.content.pm.AndroidTestBaseUpdater", + "android.content.pm.parsing.library.AndroidTestBaseUpdater", RemoveUnnecessaryAndroidTestBaseLibrary::new); PackageSharedLibraryUpdater[] updaterArray = packageUpdaters @@ -123,20 +123,20 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { } /** - * Modify the shared libraries in the supplied {@link Package} to maintain backwards + * Modify the shared libraries in the supplied {@link ParsedPackage} to maintain backwards * compatibility. * - * @param pkg the {@link Package} to modify. + * @param parsedPackage the {@link ParsedPackage} to modify. */ @VisibleForTesting - public static void modifySharedLibraries(Package pkg) { - INSTANCE.updatePackage(pkg); + public static void modifySharedLibraries(ParsedPackage parsedPackage) { + INSTANCE.updatePackage(parsedPackage); } @Override - public void updatePackage(Package pkg) { + public void updatePackage(ParsedPackage parsedPackage) { for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) { - packageUpdater.updatePackage(pkg); + packageUpdater.updatePackage(parsedPackage); } } @@ -161,10 +161,10 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { public static class AndroidTestRunnerSplitUpdater extends PackageSharedLibraryUpdater { @Override - public void updatePackage(Package pkg) { + public void updatePackage(ParsedPackage parsedPackage) { // android.test.runner has a dependency on android.test.mock so if android.test.runner // is present but android.test.mock is not then add android.test.mock. - prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK); + prefixImplicitDependency(parsedPackage, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK); } } @@ -177,8 +177,8 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { extends PackageSharedLibraryUpdater { @Override - public void updatePackage(Package pkg) { - removeLibrary(pkg, ORG_APACHE_HTTP_LEGACY); + public void updatePackage(ParsedPackage parsedPackage) { + removeLibrary(parsedPackage, ORG_APACHE_HTTP_LEGACY); } } @@ -192,8 +192,8 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { extends PackageSharedLibraryUpdater { @Override - public void updatePackage(Package pkg) { - removeLibrary(pkg, ANDROID_TEST_BASE); + public void updatePackage(ParsedPackage parsedPackage) { + removeLibrary(parsedPackage, ANDROID_TEST_BASE); } } } diff --git a/core/java/android/content/pm/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java index 1565d9ce77d4..8b27d140a8f4 100644 --- a/core/java/android/content/pm/PackageSharedLibraryUpdater.java +++ b/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.content.pm; +package android.content.pm.parsing.library; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.parsing.ParsedPackage; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; +import java.util.List; /** - * Base for classes that update a {@link PackageParser.Package}'s shared libraries. + * Base for classes that update a {@link ParsedPackage}'s shared libraries. * * @hide */ @@ -34,14 +36,13 @@ public abstract class PackageSharedLibraryUpdater { /** * Update the package's shared libraries. * - * @param pkg the package to update. + * @param parsedPackage the package to update. */ - public abstract void updatePackage(PackageParser.Package pkg); + public abstract void updatePackage(ParsedPackage parsedPackage); - static void removeLibrary(PackageParser.Package pkg, String libraryName) { - pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, libraryName); - pkg.usesOptionalLibraries = - ArrayUtils.remove(pkg.usesOptionalLibraries, libraryName); + static void removeLibrary(ParsedPackage parsedPackage, String libraryName) { + parsedPackage.removeUsesLibrary(libraryName) + .removeUsesOptionalLibrary(libraryName); } static @NonNull @@ -53,8 +54,8 @@ public abstract class PackageSharedLibraryUpdater { return cur; } - private static boolean isLibraryPresent(ArrayList<String> usesLibraries, - ArrayList<String> usesOptionalLibraries, String apacheHttpLegacy) { + private static boolean isLibraryPresent(List<String> usesLibraries, + List<String> usesOptionalLibraries, String apacheHttpLegacy) { return ArrayUtils.contains(usesLibraries, apacheHttpLegacy) || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy); } @@ -65,37 +66,32 @@ public abstract class PackageSharedLibraryUpdater { * <p>If the package has an existing dependency on {@code existingLibrary} then prefix it with * the {@code implicitDependency} if it is not already in the list of libraries. * - * @param pkg the {@link PackageParser.Package} to update. + * @param parsedPackage the {@link ParsedPackage} to update. * @param existingLibrary the existing library. * @param implicitDependency the implicit dependency to add */ - void prefixImplicitDependency(PackageParser.Package pkg, String existingLibrary, + void prefixImplicitDependency(ParsedPackage parsedPackage, String existingLibrary, String implicitDependency) { - ArrayList<String> usesLibraries = pkg.usesLibraries; - ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries; + List<String> usesLibraries = parsedPackage.getUsesLibraries(); + List<String> usesOptionalLibraries = parsedPackage.getUsesOptionalLibraries(); if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) { if (ArrayUtils.contains(usesLibraries, existingLibrary)) { - prefix(usesLibraries, implicitDependency); + parsedPackage.addUsesLibrary(0, implicitDependency); } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) { - prefix(usesOptionalLibraries, implicitDependency); + parsedPackage.addUsesOptionalLibrary(0, implicitDependency); } - - pkg.usesLibraries = usesLibraries; - pkg.usesOptionalLibraries = usesOptionalLibraries; } } - void prefixRequiredLibrary(PackageParser.Package pkg, String libraryName) { - ArrayList<String> usesLibraries = pkg.usesLibraries; - ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries; + void prefixRequiredLibrary(ParsedPackage parsedPackage, String libraryName) { + List<String> usesLibraries = parsedPackage.getUsesLibraries(); + List<String> usesOptionalLibraries = parsedPackage.getUsesOptionalLibraries(); boolean alreadyPresent = isLibraryPresent( usesLibraries, usesOptionalLibraries, libraryName); if (!alreadyPresent) { - usesLibraries = prefix(usesLibraries, libraryName); - - pkg.usesLibraries = usesLibraries; + parsedPackage.addUsesLibrary(0, libraryName); } } } diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/parsing/library/SharedLibraryNames.java index a607a9ff682b..7b691c06718e 100644 --- a/core/java/android/content/pm/SharedLibraryNames.java +++ b/core/java/android/content/pm/parsing/library/SharedLibraryNames.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.content.pm; +package android.content.pm.parsing.library; /** * A set of shared library names |
