Revert "Revert "Deprecate PackageParser#Package""
This reverts commit 8e18a0ab8980a3092d6c7e1d2edc6407655af609.
Exempt-From-Owner-Approval: Revert to re-add approved change
Change-Id: I5b5195470f365a2347cbf444cc0494d119e42ffc
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e724443..26193f6 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1376,7 +1376,8 @@
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/AndroidHidlUpdater.java b/core/java/android/content/pm/AndroidHidlUpdater.java
deleted file mode 100644
index d0657e5..0000000
--- a/core/java/android/content/pm/AndroidHidlUpdater.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-
-import android.content.pm.PackageParser.Package;
-import android.os.Build;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
- * and android.hidl.manager-V1.0-java libraries are included by default.
- *
- * @hide
- */
-@VisibleForTesting
-public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
-
- @Override
- public void updatePackage(Package pkg) {
- ApplicationInfo info = pkg.applicationInfo;
-
- // This was the default <= P and is maintained for backwards compatibility.
- boolean isLegacy = info.targetSdkVersion <= Build.VERSION_CODES.P;
- // Only system apps use these libraries
- boolean isSystem = info.isSystemApp() || info.isUpdatedSystemApp();
-
- if (isLegacy && isSystem) {
- prefixRequiredLibrary(pkg, ANDROID_HIDL_BASE);
- prefixRequiredLibrary(pkg, ANDROID_HIDL_MANAGER);
- } else {
- removeLibrary(pkg, ANDROID_HIDL_BASE);
- removeLibrary(pkg, ANDROID_HIDL_MANAGER);
- }
- }
-}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index d6fb28f..aa0002d 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -373,8 +373,9 @@
/**
* 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 cf21e96..65ee1e5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -57,6 +57,12 @@
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.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 @@
* @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 static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
/** Path prefix for apps on expanded storage */
- private static final String MNT_EXPAND = "/mnt/expand/";
+ public 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";
+ 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";
- private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+ 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 @@
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_COMPATIBLE_SCREENS);
+ CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+ CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP);
+ CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION);
+ CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS);
+ CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT);
CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION);
CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE);
- 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_SUPPORTS_INPUT);
- CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+ 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 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 @@
* @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;
- private int mParseError = PackageManager.INSTALL_SUCCEEDED;
+ public int mParseError = PackageManager.INSTALL_SUCCEEDED;
- private static boolean sCompatibilityModeEnabled = true;
- private static boolean sUseRoundIcon = false;
+ public ThreadLocal<ApkParseUtils.ParseResult> mSharedResult
+ = ThreadLocal.withInitial(ApkParseUtils.ParseResult::new);
- private static final int PARSE_DEFAULT_INSTALL_LOCATION =
+ public static boolean sCompatibilityModeEnabled = true;
+ public static boolean sUseRoundIcon = false;
+
+ 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 @@
* 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 @@
@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 @@
* 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 @@
* 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 @@
+ "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 */);
- }
-
- /**
- * 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 @@
}
@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 @@
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 @@
}
@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 @@
* 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 @@
/**
* 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 @@
* 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 @@
* 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 @@
* 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 @@
? 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/**
* 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 @@
// 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 @@
/** 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 @@
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 @@
* 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 @@
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 @@
};
}
+ /**
+ * @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 @@
}
}
+ /**
+ * @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 @@
};
}
+ /**
+ * @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
};
}
+ /**
+ * @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 @@
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 @@
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 @@
};
}
+ /**
+ * @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 @@
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 @@
};
}
+ /**
+ * @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 @@
return pi;
}
+ /**
+ * @deprecated use {@link ComponentParseUtils.ParsedInstrumentation}
+ */
+ @Deprecated
public final static class Instrumentation extends Component<IntentInfo> implements
Parcelable {
@UnsupportedAppUsage
@@ -8316,7 +8333,12 @@
};
}
+ /**
+ * @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 @@
return ii;
}
+ /**
+ * @deprecated use {@link ComponentParseUtils.ParsedIntentInfo}
+ */
+ @Deprecated
public static abstract class IntentInfo extends IntentFilter {
@UnsupportedAppUsage
public boolean hasDefault;
@@ -8371,8 +8397,10 @@
}
}
- 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 @@
}
}
+ /**
+ * @deprecated use {@link ComponentParseUtils.ParsedServiceIntentInfo}
+ */
+ @Deprecated
public final static class ServiceIntentInfo extends IntentInfo {
@UnsupportedAppUsage
public Service service;
@@ -8419,6 +8451,10 @@
}
}
+ /**
+ * @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/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
deleted file mode 100644
index 1565d9c..0000000
--- a/core/java/android/content/pm/PackageSharedLibraryUpdater.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-
-import java.util.ArrayList;
-
-/**
- * Base for classes that update a {@link PackageParser.Package}'s shared libraries.
- *
- * @hide
- */
-@VisibleForTesting
-public abstract class PackageSharedLibraryUpdater {
-
- /**
- * Update the package's shared libraries.
- *
- * @param pkg the package to update.
- */
- public abstract void updatePackage(PackageParser.Package pkg);
-
- static void removeLibrary(PackageParser.Package pkg, String libraryName) {
- pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, libraryName);
- pkg.usesOptionalLibraries =
- ArrayUtils.remove(pkg.usesOptionalLibraries, libraryName);
- }
-
- static @NonNull
- <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
- if (cur == null) {
- cur = new ArrayList<>();
- }
- cur.add(0, val);
- return cur;
- }
-
- private static boolean isLibraryPresent(ArrayList<String> usesLibraries,
- ArrayList<String> usesOptionalLibraries, String apacheHttpLegacy) {
- return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
- || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
- }
-
- /**
- * Add an implicit dependency.
- *
- * <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 existingLibrary the existing library.
- * @param implicitDependency the implicit dependency to add
- */
- void prefixImplicitDependency(PackageParser.Package pkg, String existingLibrary,
- String implicitDependency) {
- ArrayList<String> usesLibraries = pkg.usesLibraries;
- ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
- if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
- if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
- prefix(usesLibraries, implicitDependency);
- } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
- prefix(usesOptionalLibraries, implicitDependency);
- }
-
- pkg.usesLibraries = usesLibraries;
- pkg.usesOptionalLibraries = usesOptionalLibraries;
- }
- }
-
- void prefixRequiredLibrary(PackageParser.Package pkg, String libraryName) {
- ArrayList<String> usesLibraries = pkg.usesLibraries;
- ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
- boolean alreadyPresent = isLibraryPresent(
- usesLibraries, usesOptionalLibraries, libraryName);
- if (!alreadyPresent) {
- usesLibraries = prefix(usesLibraries, libraryName);
-
- pkg.usesLibraries = usesLibraries;
- }
- }
-}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 5c74efb..55574c3 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_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 @@
&& (!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 @@
* 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 @@
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 @@
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 3488cc3..2863b26 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
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 @@
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 5d10b88..4cd201f 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -22,6 +22,7 @@
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 @@
*
* 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 @@
*
* @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 0000000..ab3cf7c
--- /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 0000000..bef984d
--- /dev/null
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -0,0 +1,468 @@
+/*
+ * 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.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 ambigious 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();
+
+ ApplicationInfo toAppInfo();
+
+ 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 0000000..b7595d2
--- /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 0000000..ac2e373
--- /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 0000000..0f35b27
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -0,0 +1,3197 @@
+/*
+ * 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);
+ }
+
+ // TODO: Remove when manifest overlaying removed
+ if (callback != null) {
+ String[] overlayPaths = callback.getOverlayPaths(pkgName, apkPath);
+ if (overlayPaths != null && overlayPaths.length > 0) {
+ for (String overlayPath : overlayPaths) {
+ res.getAssets().addOverlayPath(overlayPath);
+ }
+ }
+ }
+
+ 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 0000000..adf2a4f
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -0,0 +1,3289 @@
+/*
+ * 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.SparseArray;
+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 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 protectionLevel == 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 String sourceDir;
+ public String publicSourceDir;
+ public String[] splitNames;
+ public String[] splitSourceDirs;
+ public String[] splitPublicSourceDirs;
+ public SparseArray<int[]> splitDependencies;
+ public String dataDir;
+ public String deviceProtectedDataDir;
+ public String credentialProtectedDataDir;
+ public String primaryCpuAbi;
+ public String secondaryCpuAbi;
+ public String nativeLibraryDir;
+ public String secondaryNativeLibraryDir;
+
+ 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);
+ dest.writeString(this.sourceDir);
+ dest.writeString(this.publicSourceDir);
+ dest.writeStringArray(this.splitNames);
+ dest.writeStringArray(this.splitSourceDirs);
+ dest.writeStringArray(this.splitPublicSourceDirs);
+ dest.writeSparseArray(this.splitDependencies);
+ dest.writeString(this.dataDir);
+ dest.writeString(this.deviceProtectedDataDir);
+ dest.writeString(this.credentialProtectedDataDir);
+ dest.writeString(this.primaryCpuAbi);
+ dest.writeString(this.secondaryCpuAbi);
+ dest.writeString(this.nativeLibraryDir);
+ dest.writeString(this.secondaryNativeLibraryDir);
+ }
+
+ protected ParsedInstrumentation(Parcel in) {
+ super(in);
+ // We use the boot classloader for all classes that we load.
+ final ClassLoader boot = Object.class.getClassLoader();
+ this.targetPackage = TextUtils.safeIntern(in.readString());
+ this.targetProcesses = TextUtils.safeIntern(in.readString());
+ this.handleProfiling = in.readByte() != 0;
+ this.functionalTest = in.readByte() != 0;
+ this.sourceDir = in.readString();
+ this.publicSourceDir = in.readString();
+ this.splitNames = in.createStringArray();
+ this.splitSourceDirs = in.createStringArray();
+ this.splitPublicSourceDirs = in.createStringArray();
+ this.splitDependencies = in.readSparseArray(boot);
+ this.dataDir = in.readString();
+ this.deviceProtectedDataDir = in.readString();
+ this.credentialProtectedDataDir = in.readString();
+ this.primaryCpuAbi = in.readString();
+ this.secondaryCpuAbi = in.readString();
+ this.nativeLibraryDir = in.readString();
+ this.secondaryNativeLibraryDir = in.readString();
+ }
+
+ 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 0000000..363cf80
--- /dev/null
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -0,0 +1,3213 @@
+/*
+ * 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 toAppInfo() {
+ updateFlags();
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ applicationInfo.flags = flags;
+ applicationInfo.privateFlags = privateFlags;
+ applicationInfo.sharedLibraryFiles = usesLibraryFiles;
+ applicationInfo.sharedLibraryInfos = usesLibraryInfos;
+
+ applicationInfo.appComponentFactory = appComponentFactory;
+ applicationInfo.backupAgentName = backupAgentName;
+ applicationInfo.banner = banner;
+ applicationInfo.category = category;
+ applicationInfo.classLoaderName = classLoaderName;
+ applicationInfo.className = className;
+ applicationInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
+ applicationInfo.credentialProtectedDataDir = credentialProtectedDataDir;
+ applicationInfo.dataDir = dataDir;
+ applicationInfo.descriptionRes = descriptionRes;
+ applicationInfo.deviceProtectedDataDir = deviceProtectedDataDir;
+ applicationInfo.enabled = enabled;
+ applicationInfo.fullBackupContent = fullBackupContent;
+ applicationInfo.icon = icon;
+ applicationInfo.iconRes = iconRes;
+ applicationInfo.installLocation = installLocation;
+ applicationInfo.labelRes = labelRes;
+ applicationInfo.largestWidthLimitDp = largestWidthLimitDp;
+ applicationInfo.logo = logo;
+ applicationInfo.manageSpaceActivityName = manageSpaceActivityName;
+ applicationInfo.maxAspectRatio = maxAspectRatio;
+ applicationInfo.minAspectRatio = minAspectRatio;
+ applicationInfo.minSdkVersion = minSdkVersion;
+ applicationInfo.name = name;
+ applicationInfo.nativeLibraryDir = nativeLibraryDir;
+ applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir;
+ applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+ applicationInfo.networkSecurityConfigRes = networkSecurityConfigRes;
+ applicationInfo.nonLocalizedLabel = nonLocalizedLabel;
+ applicationInfo.permission = permission;
+ applicationInfo.primaryCpuAbi = primaryCpuAbi;
+ applicationInfo.processName = processName;
+ applicationInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
+ applicationInfo.roundIconRes = roundIconRes;
+ applicationInfo.secondaryCpuAbi = secondaryCpuAbi;
+ applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+ applicationInfo.seInfo = seInfo;
+ applicationInfo.seInfoUser = seInfoUser;
+ applicationInfo.splitClassLoaderNames = splitClassLoaderNames;
+ applicationInfo.splitDependencies = splitDependencies;
+ applicationInfo.splitNames = splitNames;
+ applicationInfo.storageUuid = StorageManager.convert(applicationVolumeUuid);
+ applicationInfo.targetSandboxVersion = targetSandboxVersion;
+ applicationInfo.targetSdkVersion = targetSdkVersion;
+ applicationInfo.taskAffinity = taskAffinity;
+ applicationInfo.theme = theme;
+ applicationInfo.uid = uid;
+ applicationInfo.uiOptions = uiOptions;
+ applicationInfo.volumeUuid = applicationVolumeUuid;
+ applicationInfo.zygotePreloadName = zygotePreloadName;
+
+ applicationInfo.setBaseCodePath(baseCodePath);
+ applicationInfo.setBaseResourcePath(applicationInfoBaseResourcePath);
+ applicationInfo.setCodePath(applicationInfoCodePath);
+ applicationInfo.setResourcePath(applicationInfoResourcePath);
+ applicationInfo.setSplitCodePaths(splitCodePaths);
+ applicationInfo.setSplitResourcePaths(applicationInfoSplitResourcePaths);
+
+ return applicationInfo;
+ }
+
+ @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 0000000..7b329eb
--- /dev/null
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -0,0 +1,732 @@
+/*
+ * 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.ApplicationInfo.FLAG_SUSPENDED;
+
+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 android.os.Bundle;
+import android.os.UserHandle;
+
+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), 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;
+ }
+
+ // TODO(b/135203078): Remove this in favor of AndroidPackage.toAppInfo()
+ private static ApplicationInfo appInfoFromFinalPackage(AndroidPackage pkg) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.name = pkg.getName();
+ if (appInfo.name != null) appInfo.name = appInfo.name.trim();
+ appInfo.packageName = pkg.getPackageName();
+ appInfo.labelRes = pkg.getLabelRes();
+ appInfo.nonLocalizedLabel = pkg.getNonLocalizedLabel();
+ if (appInfo.nonLocalizedLabel != null) {
+ appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim();
+ }
+ appInfo.icon = pkg.getIcon();
+ appInfo.banner = pkg.getBanner();
+ appInfo.logo = pkg.getLogo();
+ appInfo.metaData = pkg.getMetaData();
+
+ // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo.
+// appInfo.showUserIcon = pkg.getShowUserIcon();
+
+ appInfo.taskAffinity = pkg.getTaskAffinity();
+ appInfo.permission = pkg.getPermission();
+ appInfo.processName = pkg.getProcessName();
+ appInfo.className = pkg.getClassName();
+ appInfo.theme = pkg.getTheme();
+ appInfo.flags = pkg.getFlags();
+ appInfo.privateFlags = pkg.getPrivateFlags();
+ appInfo.requiresSmallestWidthDp = pkg.getRequiresSmallestWidthDp();
+ appInfo.compatibleWidthLimitDp = pkg.getCompatibleWidthLimitDp();
+ appInfo.largestWidthLimitDp = pkg.getLargestWidthLimitDp();
+ appInfo.volumeUuid = pkg.getVolumeUuid();
+ appInfo.storageUuid = pkg.getStorageUuid();
+ appInfo.scanSourceDir = pkg.getScanSourceDir();
+ appInfo.scanPublicSourceDir = pkg.getScanPublicSourceDir();
+ appInfo.sourceDir = pkg.getBaseCodePath();
+ appInfo.publicSourceDir = pkg.getPublicSourceDir();
+ appInfo.splitNames = pkg.getSplitNames();
+ appInfo.splitSourceDirs = pkg.getSplitCodePaths();
+ appInfo.splitPublicSourceDirs = pkg.getSplitPublicSourceDirs();
+ appInfo.splitDependencies = pkg.getSplitDependencies();
+ appInfo.nativeLibraryDir = pkg.getNativeLibraryDir();
+ appInfo.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
+ appInfo.nativeLibraryRootDir = pkg.getNativeLibraryRootDir();
+ appInfo.nativeLibraryRootRequiresIsa = pkg.isNativeLibraryRootRequiresIsa();
+ appInfo.primaryCpuAbi = pkg.getPrimaryCpuAbi();
+ appInfo.secondaryCpuAbi = pkg.getSecondaryCpuAbi();
+
+ // TODO(b/135203078): Unused?
+// appInfo.resourceDirs = pkg.getResourceDirs();
+ appInfo.seInfo = pkg.getSeInfo();
+ appInfo.seInfoUser = pkg.getSeInfoUser();
+ appInfo.sharedLibraryFiles = pkg.getUsesLibraryFiles();
+ appInfo.sharedLibraryInfos = pkg.getUsesLibraryInfos();
+ appInfo.dataDir = pkg.getDataDir();
+ appInfo.deviceProtectedDataDir = pkg.getDeviceProtectedDataDir();
+ appInfo.credentialProtectedDataDir = pkg.getCredentialProtectedDataDir();
+ appInfo.uid = pkg.getUid();
+ appInfo.minSdkVersion = pkg.getMinSdkVersion();
+ appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
+ appInfo.setVersionCode(pkg.getLongVersionCode());
+ appInfo.enabled = pkg.isEnabled();
+
+ // TODO(b/135203078): Unused?
+// appInfo.enabledSetting = pkg.getEnabledSetting();
+ appInfo.installLocation = pkg.getInstallLocation();
+ appInfo.manageSpaceActivityName = pkg.getManageSpaceActivityName();
+ appInfo.descriptionRes = pkg.getDescriptionRes();
+ appInfo.uiOptions = pkg.getUiOptions();
+ appInfo.backupAgentName = pkg.getBackupAgentName();
+ appInfo.fullBackupContent = pkg.getFullBackupContent();
+ appInfo.networkSecurityConfigRes = pkg.getNetworkSecurityConfigRes();
+ appInfo.category = pkg.getCategory();
+ appInfo.targetSandboxVersion = pkg.getTargetSandboxVersion();
+ appInfo.classLoaderName = pkg.getClassLoaderName();
+ appInfo.splitClassLoaderNames = pkg.getSplitClassLoaderNames();
+ appInfo.appComponentFactory = pkg.getAppComponentFactory();
+ appInfo.iconRes = pkg.getIconRes();
+ appInfo.roundIconRes = pkg.getRoundIconRes();
+ appInfo.compileSdkVersion = pkg.getCompileSdkVersion();
+ appInfo.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+
+ // TODO(b/135203078): See PackageImpl#getHiddenApiEnforcementPolicy
+// appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy();
+ appInfo.hiddenUntilInstalled = pkg.isHiddenUntilInstalled();
+ appInfo.zygotePreloadName = pkg.getZygotePreloadName();
+ return appInfo;
+ }
+
+ 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;
+ }
+ if (!copyNeeded(flags, pkg, state, null, userId)
+ && ((flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0
+ || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ // TODO(b/135203078): This isn't applicable anymore, as AppInfo isn't cached and
+ // always built new in toAppInfo(). Remove entire copyNeeded flow? Or find a way to
+ // transiently cache AppInfo, since multiple calls in quick secession probably need
+ // the same AppInfo.
+ // In this case it is safe to directly modify the internal ApplicationInfo state:
+ // - CompatibilityMode is global state, so will be the same for every call.
+ // - We only come in to here if the app should reported as installed; this is the
+ // default state, and we will do a copy otherwise.
+ // - The enable state will always be reported the same for the application across
+ // calls; the only exception is for the UNTIL_USED mode, and in that case we will
+ // be doing a copy.
+ ApplicationInfo applicationInfo = pkg.toAppInfo();
+ updateApplicationInfo(applicationInfo, flags, state);
+ return applicationInfo;
+ }
+
+ // Make shallow copy so we can store the metadata/libraries safely
+ ApplicationInfo ai = appInfoFromFinalPackage(pkg);
+ ai.initForUser(userId);
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ ai.metaData = pkg.getAppMetaData();
+ }
+ if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
+ ai.sharedLibraryFiles = pkg.getUsesLibraryFiles();
+ ai.sharedLibraryInfos = pkg.getUsesLibraryInfos();
+ }
+ 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);
+ }
+ if (!copyNeeded(flags, pkg, state, a.metaData, userId)) {
+ updateApplicationInfo(applicationInfo, flags, state);
+ }
+ // 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);
+ }
+ if (!copyNeeded(flags, pkg, state, s.metaData, userId)) {
+ updateApplicationInfo(applicationInfo, flags, state);
+ }
+ // 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);
+ }
+ if (!copyNeeded(flags, pkg, state, p.metaData, userId)
+ && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
+ || p.uriPermissionPatterns == null)) {
+ updateApplicationInfo(applicationInfo, flags, state);
+ }
+ // 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,
+ @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 = i.sourceDir;
+ ii.publicSourceDir = i.publicSourceDir;
+ ii.splitNames = i.splitNames;
+ ii.splitSourceDirs = i.splitSourceDirs;
+ ii.splitPublicSourceDirs = i.splitPublicSourceDirs;
+ ii.splitDependencies = i.splitDependencies;
+ ii.dataDir = i.dataDir;
+ ii.deviceProtectedDataDir = i.deviceProtectedDataDir;
+ ii.credentialProtectedDataDir = i.credentialProtectedDataDir;
+ ii.primaryCpuAbi = i.primaryCpuAbi;
+ ii.secondaryCpuAbi = i.secondaryCpuAbi;
+ ii.nativeLibraryDir = i.nativeLibraryDir;
+ ii.secondaryNativeLibraryDir = i.secondaryNativeLibraryDir;
+
+ 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 boolean copyNeeded(@PackageManager.ComponentInfoFlags int flags,
+ AndroidPackage pkg, PackageUserState state, Bundle metaData, int userId) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ // We always need to copy for other users, since we need
+ // to fix up the uid.
+ return true;
+ }
+ if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ if (pkg.isEnabled() != enabled) {
+ return true;
+ }
+ }
+ boolean suspended = (pkg.getFlags() & FLAG_SUSPENDED) != 0;
+ if (state.suspended != suspended) {
+ return true;
+ }
+ if (!state.installed || state.hidden) {
+ return true;
+ }
+ if (state.stopped) {
+ return true;
+ }
+ if (state.instantApp != pkg.isInstantApp()) {
+ return true;
+ }
+ if ((flags & PackageManager.GET_META_DATA) != 0
+ && (metaData != null || pkg.getAppMetaData() != null)) {
+ return true;
+ }
+ if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+ && pkg.getUsesLibraryFiles() != null) {
+ return true;
+ }
+ if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+ && pkg.getUsesLibraryInfos() != null) {
+ return true;
+ }
+ return pkg.getStaticSharedLibName() != null;
+ }
+
+ 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 0000000..05cf586
--- /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 0000000..43c1f6e
--- /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/parsing/library/AndroidHidlUpdater.java b/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java
new file mode 100644
index 0000000..81b4bc5
--- /dev/null
+++ b/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java
@@ -0,0 +1,50 @@
+/*
+ * 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.library;
+
+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.parsing.ParsedPackage;
+import android.os.Build;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
+ * and android.hidl.manager-V1.0-java libraries are included by default.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(ParsedPackage parsedPackage) {
+ // This was the default <= P and is maintained for backwards compatibility.
+ boolean isLegacy = parsedPackage.getTargetSdkVersion() <= Build.VERSION_CODES.P;
+ // Only system apps use these libraries
+ boolean isSystem = parsedPackage.isSystemApp() || parsedPackage.isUpdatedSystemApp();
+
+ if (isLegacy && isSystem) {
+ prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_BASE);
+ prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_MANAGER);
+ } else {
+ 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
similarity index 88%
rename from core/java/android/content/pm/AndroidTestBaseUpdater.java
rename to core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java
index 18d3ba3..fc02a86 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,10 +13,10 @@
* 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;
@@ -72,7 +72,7 @@
}
@Override
- public void updatePackage(Package pkg) {
+ public void updatePackage(ParsedPackage parsedPackage) {
// 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.
@@ -82,7 +82,7 @@
// If a package already depends on android.test.runner then add a dependency on
// android.test.base because android.test.runner depends on classes from the
// android.test.base library.
- prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
+ prefixImplicitDependency(parsedPackage, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
}
}
}
diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
similarity index 67%
rename from core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
rename to core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
index 707443b..613a06b 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 @@
@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
similarity index 80%
rename from core/java/android/content/pm/PackageBackwardCompatibility.java
rename to core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java
index 4331bd4..1220fc4 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.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 @@
// 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 @@
}
/**
- * 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 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 @@
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 @@
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/parsing/library/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java
new file mode 100644
index 0000000..8b27d14
--- /dev/null
+++ b/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java
@@ -0,0 +1,97 @@
+/*
+ * 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.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 ParsedPackage}'s shared libraries.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public abstract class PackageSharedLibraryUpdater {
+
+ /**
+ * Update the package's shared libraries.
+ *
+ * @param parsedPackage the package to update.
+ */
+ public abstract void updatePackage(ParsedPackage parsedPackage);
+
+ static void removeLibrary(ParsedPackage parsedPackage, String libraryName) {
+ parsedPackage.removeUsesLibrary(libraryName)
+ .removeUsesOptionalLibrary(libraryName);
+ }
+
+ static @NonNull
+ <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
+ if (cur == null) {
+ cur = new ArrayList<>();
+ }
+ cur.add(0, val);
+ return cur;
+ }
+
+ private static boolean isLibraryPresent(List<String> usesLibraries,
+ List<String> usesOptionalLibraries, String apacheHttpLegacy) {
+ return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
+ || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
+ }
+
+ /**
+ * Add an implicit dependency.
+ *
+ * <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 parsedPackage the {@link ParsedPackage} to update.
+ * @param existingLibrary the existing library.
+ * @param implicitDependency the implicit dependency to add
+ */
+ void prefixImplicitDependency(ParsedPackage parsedPackage, String existingLibrary,
+ String implicitDependency) {
+ List<String> usesLibraries = parsedPackage.getUsesLibraries();
+ List<String> usesOptionalLibraries = parsedPackage.getUsesOptionalLibraries();
+
+ if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
+ if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
+ parsedPackage.addUsesLibrary(0, implicitDependency);
+ } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
+ parsedPackage.addUsesOptionalLibrary(0, implicitDependency);
+ }
+ }
+ }
+
+ void prefixRequiredLibrary(ParsedPackage parsedPackage, String libraryName) {
+ List<String> usesLibraries = parsedPackage.getUsesLibraries();
+ List<String> usesOptionalLibraries = parsedPackage.getUsesOptionalLibraries();
+
+ boolean alreadyPresent = isLibraryPresent(
+ usesLibraries, usesOptionalLibraries, libraryName);
+ if (!alreadyPresent) {
+ parsedPackage.addUsesLibrary(0, libraryName);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/parsing/library/SharedLibraryNames.java
similarity index 91%
rename from core/java/android/content/pm/SharedLibraryNames.java
rename to core/java/android/content/pm/parsing/library/SharedLibraryNames.java
index a607a9f..7b691c0 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
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 0847fbd..6709ff5 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -28,9 +28,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.os.SELinux;
import android.system.ErrnoException;
@@ -87,11 +87,12 @@
}
}
- public static Handle create(Package pkg) throws IOException {
- return create(pkg.getAllCodePaths(),
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+ public static Handle create(AndroidPackage pkg) throws IOException {
+ return create(
+ pkg.makeListAllCodePaths(),
+ (pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) != 0,
+ (pkg.getFlags() & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
+ (pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
}
public static Handle create(PackageLite lite) throws IOException {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 821022f..bc80197 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -600,6 +600,14 @@
return cur;
}
+ public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, int index, T val) {
+ if (cur == null) {
+ cur = new ArrayList<>();
+ }
+ cur.add(index, val);
+ return cur;
+ }
+
public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
if (cur == null) {
return null;
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index a4c504b..c009f58 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -74,7 +74,6 @@
":FrameworksCoreTests_install_loc_internal",
":FrameworksCoreTests_install_loc_sdcard",
":FrameworksCoreTests_install_loc_unspecified",
- ":FrameworksCoreTests_install_multi_package",
":FrameworksCoreTests_install_split_base",
":FrameworksCoreTests_install_split_feature_a",
":FrameworksCoreTests_install_use_perm_good",
diff --git a/core/tests/coretests/apks/install_multi_package/Android.bp b/core/tests/coretests/apks/install_multi_package/Android.bp
deleted file mode 100644
index 249242e..0000000
--- a/core/tests/coretests/apks/install_multi_package/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-android_test_helper_app {
- name: "FrameworksCoreTests_install_multi_package",
- defaults: ["FrameworksCoreTests_apks_defaults"],
-
- srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml b/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml
deleted file mode 100644
index 5164cae..0000000
--- a/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_multi_package">
-
-<!--
- This manifest is has child packages with components.
--->
-
- <uses-feature
- android:name="com.android.frameworks.coretests.nonexistent" />
- <uses-configuration
- android:reqFiveWayNav="false" />
-
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.coretests"
- android:label="Frameworks Core Tests" />
-
- <permission
- android:label="test permission"
- android:name="test_permission"
- android:protectionLevel="normal" />
- <uses-permission android:name="android.permission.INTERNET" />
-
-<!--
- NOTE: This declares a child package, application, then another child
- package, to test potential bugs that are order-dependent. Also, each
- one varies the order.
--->
-
- <package package="com.android.frameworks.coretests.install_multi_package.first_child">
- <uses-permission android:name="android.permission.NFC" />
- <!-- NOTE: A declared permission is ignored since the tag is not whitelisted. -->
- <permission
- android:label="test permission"
- android:name="first_child_permission"
- android:protectionLevel="signature" />
- <application
- android:hasCode="true">
- <activity
- android:name="com.android.frameworks.coretests.FirstChildTestActivity">
- </activity>
- <provider
- android:name="com.android.frameworks.coretests.FirstChildTestProvider"
- android:authorities="com.android.frameworks.coretests.testprovider" />
- <receiver
- android:name="com.android.frameworks.coretests.FirstChildTestReceiver" />
- <service
- android:name="com.android.frameworks.coretests.FirstChildTestService" />
- </application>
- </package>
-
- <application
- android:hasCode="true">
- <service
- android:name="com.android.frameworks.coretests.TestService" />
- <activity
- android:name="com.android.frameworks.coretests.TestActivity">
- </activity>
- <provider
- android:name="com.android.frameworks.coretests.TestProvider"
- android:authorities="com.android.frameworks.coretests.testprovider" />
- <receiver
- android:name="com.android.frameworks.coretests.TestReceiver" />
- </application>
-
- <package package="com.android.frameworks.coretests.blah.second_child">
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS" />
- <!-- NOTE: A declared permission is ignored since the tag is not whitelisted. -->
- <permission
- android:label="test permission"
- android:name="second_child_permission"
- android:protectionLevel="dangerous" />
- <application
- android:hasCode="true">
- <receiver
- android:name="com.android.frameworks.coretests.SecondChildTestReceiver" />
- <service
- android:name="com.android.frameworks.coretests.SecondChildTestService" />
- <activity
- android:name="com.android.frameworks.coretests.SecondChildTestActivity">
- </activity>
- <provider
- android:name="com.android.frameworks.coretests.SecondChildTestProvider"
- android:authorities="com.android.frameworks.coretests.testprovider" />
- </application>
- </package>
-</manifest>
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java
deleted file mode 100644
index 57afcb0..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Activity;
-
-public class FirstChildTestActivity extends Activity {
-
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java
deleted file mode 100644
index 2816865..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class FirstChildTestProvider extends ContentProvider {
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java
deleted file mode 100644
index ffe84b7..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class FirstChildTestReceiver extends ContentProvider {
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java
deleted file mode 100644
index faa6e9c..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class FirstChildTestService extends Service {
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java
deleted file mode 100644
index e89f264..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Activity;
-
-public class SecondChildTestActivity extends Activity {
-
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java
deleted file mode 100644
index 2bd40a5..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class SecondChildTestProvider extends ContentProvider {
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java
deleted file mode 100644
index a6c4ddc..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class SecondChildTestReceiver extends ContentProvider {
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java
deleted file mode 100644
index 1e721aa..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class SecondChildTestService extends Service {
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
deleted file mode 100644
index 10d0551..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Activity;
-
-public class TestActivity extends Activity {
-
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java
deleted file mode 100644
index 59f9f10..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class TestProvider extends ContentProvider {
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java
deleted file mode 100644
index 21f6263..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class TestReceiver extends ContentProvider {
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java
deleted file mode 100644
index b330e75..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class TestService extends Service {
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
deleted file mode 100644
index cc48239..0000000
--- a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link AndroidHidlUpdater}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
- private static final String OTHER_LIBRARY = "other.library";
-
- @Test
- public void targeted_at_P() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.P);
-
- // no change, not system
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_P_system() {
- PackageBuilder before = builder().asSystemApp()
- .targetSdkVersion(Build.VERSION_CODES.P);
-
- // Should add both HIDL libraries
- PackageBuilder after = builder().asSystemApp()
- .targetSdkVersion(Build.VERSION_CODES.P)
- .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_P_not_empty_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.P)
- .requiredLibraries(OTHER_LIBRARY);
-
- // no change, not system
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_P_not_empty_usesLibraries_system() {
- PackageBuilder before = builder().asSystemApp()
- .targetSdkVersion(Build.VERSION_CODES.P)
- .requiredLibraries(OTHER_LIBRARY);
-
- // The hidl jars should be added at the start of the list because it
- // is not on the bootclasspath and the package targets pre-P.
- PackageBuilder after = builder().asSystemApp()
- .targetSdkVersion(Build.VERSION_CODES.P)
- .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE, OTHER_LIBRARY);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_P_in_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.P)
- .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
-
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.P);
-
- // Libraries are removed because they are not available for non-system apps
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_P_in_usesLibraries_system() {
- PackageBuilder before = builder().asSystemApp()
- .targetSdkVersion(Build.VERSION_CODES.P)
- .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
-
- // No change is required because the package explicitly requests the HIDL libraries
- // and is targeted at the current version so does not need backwards compatibility.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void in_usesLibraries() {
- PackageBuilder before = builder().requiredLibraries(ANDROID_HIDL_BASE);
-
- // Dependency is removed, it is not available.
- PackageBuilder after = builder();
-
- // Libraries are removed because they are not available for apps targetting Q+
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_usesOptionalLibraries() {
- PackageBuilder before = builder().optionalLibraries(ANDROID_HIDL_BASE);
-
- // Dependency is removed, it is not available.
- PackageBuilder after = builder();
-
- // Libraries are removed because they are not available for apps targetting Q+
- checkBackwardsCompatibility(before, after);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
deleted file mode 100644
index 03108ce..0000000
--- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for {@link AndroidTestBaseUpdater}
- */
-@SmallTest
-@RunWith(OptionalClassRunner.class)
-@OptionalClassRunner.OptionalClass("android.content.pm.AndroidTestBaseUpdater")
-public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
- private static final String OTHER_LIBRARY = "other.library";
-
- @Test
- public void targeted_at_Q() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q);
-
- // Should add org.apache.http.legacy.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TEST_BASE);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_Q_not_empty_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(OTHER_LIBRARY);
-
- // The org.apache.http.legacy jar should be added at the start of the list because it
- // is not on the bootclasspath and the package targets pre-Q.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TEST_BASE, OTHER_LIBRARY);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_Q_in_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TEST_BASE);
-
- // No change is required because although org.apache.http.legacy has been removed from
- // the bootclasspath the package explicitly requests it.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_Q_in_usesOptionalLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .optionalLibraries(ANDROID_TEST_BASE);
-
- // No change is required because although org.apache.http.legacy has been removed from
- // the bootclasspath the package explicitly requests it.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void in_usesLibraries() {
- PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_BASE);
-
- // No change is required because the package explicitly requests org.apache.http.legacy
- // and is targeted at the current version so does not need backwards compatibility.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void in_usesOptionalLibraries() {
- PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_BASE);
-
- // No change is required because the package explicitly requests org.apache.http.legacy
- // and is targeted at the current version so does not need backwards compatibility.
- checkBackwardsCompatibility(before, before);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- checkBackwardsCompatibility(before, after, AndroidTestBaseUpdater::new);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
deleted file mode 100644
index 7f817d6..0000000
--- a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
-
-import android.content.pm.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link AndroidTestRunnerSplitUpdater}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class AndroidTestRunnerSplitUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
- @Test
- public void android_test_runner_in_usesOptionalLibraries() {
- PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_RUNNER);
-
- PackageBuilder after = builder()
- .optionalLibraries(ANDROID_TEST_MOCK, ANDROID_TEST_RUNNER);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
- PackageBuilder before = builder()
- .requiredLibraries(ANDROID_TEST_RUNNER)
- .optionalLibraries(ANDROID_TEST_MOCK);
-
- PackageBuilder after = builder()
- .requiredLibraries(ANDROID_TEST_RUNNER)
- .optionalLibraries(ANDROID_TEST_MOCK);
-
- checkBackwardsCompatibility(before, after);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- checkBackwardsCompatibility(before, after, AndroidTestRunnerSplitUpdater::new);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
deleted file mode 100644
index 834a0bb..0000000
--- a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for {@link OrgApacheHttpLegacyUpdater}
- */
-@SmallTest
-@RunWith(OptionalClassRunner.class)
-@OptionalClassRunner.OptionalClass("android.content.pm.OrgApacheHttpLegacyUpdater")
-public class OrgApacheHttpLegacyUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
- private static final String OTHER_LIBRARY = "other.library";
-
- @Test
- public void targeted_at_O() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- // Should add org.apache.http.legacy.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_O_not_empty_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(OTHER_LIBRARY);
-
- // The org.apache.http.legacy jar should be added at the start of the list because it
- // is not on the bootclasspath and the package targets pre-P.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(ORG_APACHE_HTTP_LEGACY, OTHER_LIBRARY);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_O_in_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // No change is required because although org.apache.http.legacy has been removed from
- // the bootclasspath the package explicitly requests it.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_O_in_usesOptionalLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // No change is required because although org.apache.http.legacy has been removed from
- // the bootclasspath the package explicitly requests it.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void in_usesLibraries() {
- PackageBuilder before = builder().requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // No change is required because the package explicitly requests org.apache.http.legacy
- // and is targeted at the current version so does not need backwards compatibility.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void in_usesOptionalLibraries() {
- PackageBuilder before = builder().optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // No change is required because the package explicitly requests org.apache.http.legacy
- // and is targeted at the current version so does not need backwards compatibility.
- checkBackwardsCompatibility(before, before);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- checkBackwardsCompatibility(before, after, OrgApacheHttpLegacyUpdater::new);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
deleted file mode 100644
index ad9814b..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import static android.content.pm.PackageBuilder.builder;
-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 android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Assume;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@SmallTest
-@RunWith(JUnit4.class)
-public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdaterTest {
-
- @Test
- public void null_usesLibraries_and_usesOptionalLibraries() {
- PackageBuilder before = builder();
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- /**
- * Detect when the android.test.base is not on the bootclasspath.
- *
- * <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
- * succeed otherwise. This allows a developer to ensure that the tests are being run in the
- * correct environment.
- */
- @Test
- public void detectWhenATBisOnBCP() {
- Assume.assumeTrue(PackageBackwardCompatibility.bootClassPathContainsATB());
- }
-
- /**
- * Ensures that the {@link PackageBackwardCompatibility} uses {@link OrgApacheHttpLegacyUpdater}
- * and {@link AndroidTestBaseUpdater} when necessary.
- *
- * <p>More comprehensive tests for that class can be found in
- * {@link OrgApacheHttpLegacyUpdaterTest}.
- */
- @Test
- public void targeted_at_O() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- List<String> expected = new ArrayList<>();
- if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
- expected.add(ANDROID_TEST_BASE);
- }
- expected.add(ORG_APACHE_HTTP_LEGACY);
-
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(expected);
-
- checkBackwardsCompatibility(before, after);
- }
-
- /**
- * Ensures that the {@link PackageBackwardCompatibility} uses
- * {@link RemoveUnnecessaryAndroidTestBaseLibrary}
- * when necessary.
- *
- * <p>More comprehensive tests for that class can be found in
- * {@link RemoveUnnecessaryAndroidTestBaseLibraryTest}.
- */
- @Test
- public void android_test_base_in_usesLibraries() {
- Assume.assumeTrue("Test requires that "
- + ANDROID_TEST_BASE + " is on the bootclasspath",
- PackageBackwardCompatibility.bootClassPathContainsATB());
-
- PackageBuilder before = builder()
- .requiredLibraries(ANDROID_TEST_BASE);
-
- // android.test.base should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- /**
- * Ensures that the {@link PackageBackwardCompatibility} uses a
- * {@link PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater}.
- *
- * <p>More comprehensive tests for that class can be found in
- * {@link AndroidTestRunnerSplitUpdaterTest}.
- */
- @Test
- public void android_test_runner_in_usesLibraries() {
- PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_RUNNER);
-
- List<String> expected = new ArrayList<>();
- if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
- expected.add(ANDROID_TEST_BASE);
- }
- expected.add(ANDROID_TEST_MOCK);
- expected.add(ANDROID_TEST_RUNNER);
-
- PackageBuilder after = builder()
- .requiredLibraries(expected);
-
- checkBackwardsCompatibility(before, after);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
deleted file mode 100644
index f7544af..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Build;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Test support for building {@link PackageParser.Package} instances.
- */
-class PackageBuilder {
-
- private int mTargetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-
- private int mFlags = 0;
-
- private ArrayList<String> mRequiredLibraries;
-
- private ArrayList<String> mOptionalLibraries;
-
- public static PackageBuilder builder() {
- return new PackageBuilder();
- }
-
- public PackageParser.Package build() {
- PackageParser.Package pkg = new PackageParser.Package("org.package.name");
- pkg.applicationInfo.targetSdkVersion = mTargetSdkVersion;
- pkg.applicationInfo.flags = mFlags;
- pkg.usesLibraries = mRequiredLibraries;
- pkg.usesOptionalLibraries = mOptionalLibraries;
- return pkg;
- }
-
- PackageBuilder targetSdkVersion(int version) {
- this.mTargetSdkVersion = version;
- return this;
- }
-
- PackageBuilder asSystemApp() {
- this.mFlags |= ApplicationInfo.FLAG_SYSTEM;
- return this;
- }
-
- PackageBuilder requiredLibraries(String... names) {
- this.mRequiredLibraries = arrayListOrNull(names);
- return this;
- }
-
- PackageBuilder requiredLibraries(List<String> names) {
- this.mRequiredLibraries = arrayListOrNull(names.toArray(new String[names.size()]));
- return this;
- }
-
- PackageBuilder optionalLibraries(String... names) {
- this.mOptionalLibraries = arrayListOrNull(names);
- return this;
- }
-
- /**
- * Check that this matches the supplied {@link PackageParser.Package}.
- *
- * @param pkg the instance to compare with this.
- */
- public void check(PackageParser.Package pkg) {
- assertEquals("targetSdkVersion should not be changed",
- mTargetSdkVersion,
- pkg.applicationInfo.targetSdkVersion);
- assertEquals("usesLibraries not updated correctly",
- mRequiredLibraries,
- pkg.usesLibraries);
- assertEquals("usesOptionalLibraries not updated correctly",
- mOptionalLibraries,
- pkg.usesOptionalLibraries);
- }
-
- private static ArrayList<String> arrayListOrNull(String... strings) {
- if (strings == null || strings.length == 0) {
- return null;
- }
- ArrayList<String> list = new ArrayList<>();
- Collections.addAll(list, strings);
- return list;
- }
-
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 5c7f2af..cb23850 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -23,26 +23,26 @@
import android.apex.ApexInfo;
import android.content.Context;
-import android.content.pm.PackageParser.Component;
-import android.content.pm.PackageParser.Package;
-import android.content.pm.PackageParser.Permission;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
-import android.os.SystemProperties;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.coretests.R;
+import com.android.internal.util.ArrayUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.InputStream;
-import java.util.Arrays;
import java.util.function.Function;
@SmallTest
@@ -59,8 +59,8 @@
private static final String PRE_RELEASE_WITH_FINGERPRINT = "B.fingerprint";
private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "C.fingerprint";
- private static final String[] CODENAMES_RELEASED = { /* empty */ };
- private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
+ private static final String[] CODENAMES_RELEASED = { /* empty */};
+ private static final String[] CODENAMES_PRE_RELEASE = {PRE_RELEASE};
private static final int OLDER_VERSION = 10;
private static final int PLATFORM_VERSION = 20;
@@ -300,10 +300,6 @@
assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
}
- Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
- return parsePackage(apkFileName, apkResourceId, p -> p);
- }
-
/**
* Copies a specified {@code resourceId} to a file. Returns a non-null file if the copy
* succeeded, or {@code null} otherwise.
@@ -331,16 +327,17 @@
*
* APKs are put into coretests/apks/packageparser_*.
*
- * @param apkFileName temporary file name to store apk extracted from resources
+ * @param apkFileName temporary file name to store apk extracted from resources
* @param apkResourceId identifier of the apk as a resource
*/
- Package parsePackage(String apkFileName, int apkResourceId,
- Function<Package, Package> converter) throws Exception {
+ ParsedPackage parsePackage(String apkFileName, int apkResourceId,
+ Function<ParsedPackage, ParsedPackage> converter) throws Exception {
// Copy the resource to a file.
File outFile = null;
try {
outFile = copyRawResourceToFile(apkFileName, apkResourceId);
- return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */));
+ return converter.apply(
+ new PackageParser().parseParsedPackage(outFile, 0 /* flags */, false));
} finally {
if (outFile != null) {
outFile.delete();
@@ -351,40 +348,40 @@
/**
* Asserts basic properties about a component.
*/
- private void assertComponent(String className, String packageName, int numIntents,
- Component<?> component) {
+ private void assertComponent(String className, int numIntents, ParsedComponent<?> component) {
assertEquals(className, component.className);
- assertEquals(packageName, component.owner.packageName);
assertEquals(numIntents, component.intents.size());
}
/**
* Asserts four regularly-named components of each type: one Activity, one Service, one
* Provider, and one Receiver.
+ *
* @param template templated string with %s subbed with Activity, Service, Provider, Receiver
*/
- private void assertOneComponentOfEachType(String template, Package p) {
- String packageName = p.packageName;
+ private void assertOneComponentOfEachType(String template, AndroidPackage p) {
+ assertEquals(2, p.getActivities().size());
- assertEquals(1, p.activities.size());
+ // For normal apps, a Activity that forwards to the App Details page is added.
+ assertEquals("android.app.AppDetailsActivity", p.getActivities().get(1)
+ .className);
+
assertComponent(String.format(template, "Activity"),
- packageName, 0 /* intents */, p.activities.get(0));
- assertEquals(1, p.services.size());
+ 0 /* intents */, p.getActivities().get(0));
+ assertEquals(1, p.getServices().size());
assertComponent(String.format(template, "Service"),
- packageName, 0 /* intents */, p.services.get(0));
- assertEquals(1, p.providers.size());
+ 0 /* intents */, p.getServices().get(0));
+ assertEquals(1, p.getProviders().size());
assertComponent(String.format(template, "Provider"),
- packageName, 0 /* intents */, p.providers.get(0));
- assertEquals(1, p.receivers.size());
+ 0 /* intents */, p.getProviders().get(0));
+ assertEquals(1, p.getReceivers().size());
assertComponent(String.format(template, "Receiver"),
- packageName, 0 /* intents */, p.receivers.get(0));
+ 0 /* intents */, p.getReceivers().get(0));
}
- private void assertPermission(String name, String packageName, int protectionLevel,
- Permission permission) {
- assertEquals(packageName, permission.owner.packageName);
- assertEquals(name, permission.info.name);
- assertEquals(protectionLevel, permission.info.protectionLevel);
+ private void assertPermission(String name, int protectionLevel, ParsedPermission permission) {
+ assertEquals(name, permission.getName());
+ assertEquals(protectionLevel, permission.getProtection());
}
private void assertMetadata(Bundle b, String... keysAndValues) {
@@ -416,25 +413,25 @@
}
private void checkPackageWithComponents(
- Function<Package, Package> converter) throws Exception {
- Package p = parsePackage(
+ Function<ParsedPackage, ParsedPackage> converter) throws Exception {
+ ParsedPackage p = parsePackage(
"install_complete_package_info.apk", R.raw.install_complete_package_info,
converter);
String packageName = "com.android.frameworks.coretests.install_complete_package_info";
- assertEquals(packageName, p.packageName);
- assertEquals(1, p.permissions.size());
+ assertEquals(packageName, p.getPackageName());
+ assertEquals(1, p.getPermissions().size());
assertPermission(
"com.android.frameworks.coretests.install_complete_package_info.test_permission",
- packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0));
+ PermissionInfo.PROTECTION_NORMAL, p.getPermissions().get(0));
// Hidden "app details" activity is added to every package.
boolean foundAppDetailsActivity = false;
- for (int i = 0; i < p.activities.size(); i++) {
- if (p.activities.get(i).className.equals(
+ for (int i = 0; i < ArrayUtils.size(p.getActivities()); i++) {
+ if (p.getActivities().get(i).className.equals(
PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME)) {
foundAppDetailsActivity = true;
- p.activities.remove(i);
+ p.getActivities().remove(i);
break;
}
}
@@ -442,72 +439,23 @@
assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p);
- assertMetadata(p.mAppMetaData,
+ assertMetadata(p.getAppMetaData(),
"key1", "value1",
"key2", "this_is_app");
- assertMetadata(p.activities.get(0).metaData,
+ assertMetadata(p.getActivities().get(0).getMetaData(),
"key1", "value1",
"key2", "this_is_activity");
- assertMetadata(p.services.get(0).metaData,
+ assertMetadata(p.getServices().get(0).getMetaData(),
"key1", "value1",
"key2", "this_is_service");
- assertMetadata(p.receivers.get(0).metaData,
+ assertMetadata(p.getReceivers().get(0).getMetaData(),
"key1", "value1",
"key2", "this_is_receiver");
- assertMetadata(p.providers.get(0).metaData,
+ assertMetadata(p.getProviders().get(0).getMetaData(),
"key1", "value1",
"key2", "this_is_provider");
}
- /**
- * Determines if the current device supports multi-package APKs.
- */
- private boolean supportsMultiPackageApk() {
- return SystemProperties.getBoolean("persist.sys.child_packages_enabled", false);
- }
-
- @Test
- public void testMultiPackageComponents() throws Exception {
- // TODO(gboyer): Remove once we decide to launch multi-package APKs.
- if (!supportsMultiPackageApk()) {
- return;
- }
- String parentName = "com.android.frameworks.coretests.install_multi_package";
- String firstChildName =
- "com.android.frameworks.coretests.install_multi_package.first_child";
- String secondChildName = // NOTE: intentionally inconsistent!
- "com.android.frameworks.coretests.blah.second_child";
-
- Package parent = parsePackage("install_multi_package.apk", R.raw.install_multi_package);
- assertEquals(parentName, parent.packageName);
- assertEquals(2, parent.childPackages.size());
- assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", parent);
- assertEquals(1, parent.permissions.size());
- assertPermission(parentName + ".test_permission", parentName,
- PermissionInfo.PROTECTION_NORMAL, parent.permissions.get(0));
- assertEquals(Arrays.asList("android.permission.INTERNET"),
- parent.requestedPermissions);
-
- Package firstChild = parent.childPackages.get(0);
- assertEquals(firstChildName, firstChild.packageName);
- assertOneComponentOfEachType(
- "com.android.frameworks.coretests.FirstChildTest%s", firstChild);
- assertEquals(0, firstChild.permissions.size()); // Child APKs cannot declare permissions.
- assertEquals(Arrays.asList("android.permission.NFC"),
- firstChild.requestedPermissions);
-
- Package secondChild = parent.childPackages.get(1);
- assertEquals(secondChildName, secondChild.packageName);
- assertOneComponentOfEachType(
- "com.android.frameworks.coretests.SecondChildTest%s", secondChild);
- assertEquals(0, secondChild.permissions.size()); // Child APKs cannot declare permissions.
- assertEquals(
- Arrays.asList(
- "android.permission.ACCESS_NETWORK_STATE",
- "android.permission.READ_CONTACTS"),
- secondChild.requestedPermissions);
- }
-
@Test
public void testApexPackageInfoGeneration() throws Exception {
String apexModuleName = "com.android.tzdata.apex";
@@ -522,7 +470,7 @@
int flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES;
PackageParser pp = new PackageParser();
- Package p = pp.parsePackage(apexFile, flags, false);
+ PackageParser.Package p = pp.parsePackage(apexFile, flags, false);
PackageParser.collectCertificates(p, false);
PackageInfo pi = PackageParser.generatePackageInfo(p, apexInfo, flags);
diff --git a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
deleted file mode 100644
index 71a0e5e..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import java.util.function.Supplier;
-
-/**
- * Helper for classes that test {@link PackageSharedLibraryUpdater}.
- */
-abstract class PackageSharedLibraryUpdaterTest {
-
- static void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after,
- Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
- PackageParser.Package pkg = before.build();
- updaterSupplier.get().updatePackage(pkg);
- after.check(pkg);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
deleted file mode 100644
index 216b0c8..0000000
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-
-import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link RemoveUnnecessaryAndroidTestBaseLibrary}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class RemoveUnnecessaryAndroidTestBaseLibraryTest
- extends PackageSharedLibraryUpdaterTest {
-
- private static final String OTHER_LIBRARY = "other.library";
-
- @Test
- public void targeted_at_O() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- // No change required.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_O_not_empty_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(OTHER_LIBRARY);
-
- // No change required.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_O_in_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(ANDROID_TEST_BASE);
-
- // android.test.base should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_O_in_usesOptionalLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .optionalLibraries(ANDROID_TEST_BASE);
-
- // android.test.base should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_usesLibraries() {
- PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_BASE);
-
- // android.test.base should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_usesOptionalLibraries() {
- PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_BASE);
-
- // android.test.base should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_bothLibraries() {
- PackageBuilder before = builder()
- .requiredLibraries(ANDROID_TEST_BASE)
- .optionalLibraries(ANDROID_TEST_BASE);
-
- // android.test.base should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
- // PackageBackwardCompatibility and that seems to create a package-private lambda in
- // android.content.pm which this then tries to reuse but fails because it cannot access
- // package-private classes/members because the test is loaded by a different ClassLoader
- // than the lambda.
- checkBackwardsCompatibility(before, after,
- () -> new RemoveUnnecessaryAndroidTestBaseLibrary());
- }
-
-}
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
deleted file mode 100644
index fc60980..0000000
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-
-import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link RemoveUnnecessaryOrgApacheHttpLegacyLibrary}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest
- extends PackageSharedLibraryUpdaterTest {
-
- private static final String OTHER_LIBRARY = "other.library";
-
- @Test
- public void targeted_at_O() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- // No change required.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_O_not_empty_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(OTHER_LIBRARY);
-
- // No change required.
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_O_in_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // org.apache.http.legacy should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_O_in_usesOptionalLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.O)
- .optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // org.apache.http.legacy should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.O);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_usesLibraries() {
- PackageBuilder before = builder().requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // org.apache.http.legacy should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_usesOptionalLibraries() {
- PackageBuilder before = builder().optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // org.apache.http.legacy should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void in_bothLibraries() {
- PackageBuilder before = builder()
- .requiredLibraries(ORG_APACHE_HTTP_LEGACY)
- .optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
- // org.apache.http.legacy should be removed from the libraries because it is provided
- // on the bootclasspath and providing both increases start up cost unnecessarily.
- PackageBuilder after = builder();
-
- checkBackwardsCompatibility(before, after);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
- // PackageBackwardCompatibility and that seems to create a package-private lambda in
- // android.content.pm which this then tries to reuse but fails because it cannot access
- // package-private classes/members because the test is loaded by a different ClassLoader
- // than the lambda.
- checkBackwardsCompatibility(before, after,
- () -> new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
index 49849ee..1e0bfb0 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -25,9 +25,9 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ParsedPackage;
import android.os.FileUtils;
import androidx.test.InstrumentationRegistry;
@@ -36,7 +36,6 @@
import com.android.frameworks.coretests.R;
-import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -95,13 +94,13 @@
public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk");
- Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
assertEquals(1, packageDexMetadata.size());
- String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+ String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
assertNotNull(baseDexMetadata);
- assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+ assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
}
@Test
@@ -111,17 +110,17 @@
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
createDexMetadataFile("install_split_feature_a.apk");
- Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
assertEquals(2, packageDexMetadata.size());
- String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+ String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
assertNotNull(baseDexMetadata);
- assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+ assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
- String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+ String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
assertNotNull(splitDexMetadata);
- assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+ assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
}
@Test
@@ -130,14 +129,14 @@
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_feature_a.apk");
- Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
assertEquals(1, packageDexMetadata.size());
- String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+ String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
assertNotNull(splitDexMetadata);
- assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+ assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
}
@Test
@@ -146,7 +145,8 @@
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
Files.createFile(invalidDmFile.toPath());
try {
- PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ ParsedPackage pkg = new PackageParser()
+ .parseParsedPackage(mTmpDir, 0 /* flags */, false);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
@@ -163,7 +163,8 @@
Files.createFile(invalidDmFile.toPath());
try {
- PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ ParsedPackage pkg = new PackageParser()
+ .parseParsedPackage(mTmpDir, 0 /* flags */, false);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java
new file mode 100644
index 0000000..21479c0
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.library;
+
+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.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidHidlUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ private static final String OTHER_LIBRARY = "other.library";
+
+ @Test
+ public void targeted_at_P() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // no change, not system
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_P_system() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .hideAsParsed()
+ .setSystem(true);
+
+ // Should add both HIDL libraries
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(ANDROID_HIDL_MANAGER)
+ .addUsesLibrary(ANDROID_HIDL_BASE)
+ .hideAsParsed()
+ .setSystem(true)
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_P_not_empty_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // no change, not system
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_P_not_empty_usesLibraries_system() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .setSystem(true);
+
+ // The hidl jars should be added at the start of the list because it
+ // is not on the bootclasspath and the package targets pre-P.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(ANDROID_HIDL_MANAGER)
+ .addUsesLibrary(ANDROID_HIDL_BASE)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .setSystem(true)
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_P_in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(ANDROID_HIDL_MANAGER)
+ .addUsesLibrary(ANDROID_HIDL_BASE)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // Libraries are removed because they are not available for non-system apps
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_P_in_usesLibraries_system() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(ANDROID_HIDL_MANAGER)
+ .addUsesLibrary(ANDROID_HIDL_BASE)
+ .hideAsParsed()
+ .setSystem(true);
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.P)
+ .addUsesLibrary(ANDROID_HIDL_MANAGER)
+ .addUsesLibrary(ANDROID_HIDL_BASE)
+ .hideAsParsed()
+ .setSystem(true)
+ .hideAsFinal();
+
+ // No change is required because the package explicitly requests the HIDL libraries
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_HIDL_BASE)
+ .hideAsParsed();
+
+ // Dependency is removed, it is not available.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // Libraries are removed because they are not available for apps targeting Q+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ANDROID_HIDL_BASE)
+ .hideAsParsed();
+
+ // Dependency is removed, it is not available.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // Libraries are removed because they are not available for apps targeting Q+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java
new file mode 100644
index 0000000..65ae219
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+
+import android.content.pm.OptionalClassRunner;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link AndroidTestBaseUpdater}
+ */
+@SmallTest
+@RunWith(OptionalClassRunner.class)
+@OptionalClassRunner.OptionalClass("android.content.pm.parsing.library.AndroidTestBaseUpdater")
+public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ private static final String OTHER_LIBRARY = "other.library";
+
+ @Test
+ public void targeted_at_Q() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .hideAsParsed();
+
+ // Should add org.apache.http.legacy.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_Q_not_empty_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed();
+
+ // The org.apache.http.legacy jar should be added at the start of the list because it
+ // is not on the bootclasspath and the package targets pre-Q.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_Q_in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because although org.apache.http.legacy has been removed from
+ // the bootclasspath the package explicitly requests it.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_Q_in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because although org.apache.http.legacy has been removed from
+ // the bootclasspath the package explicitly requests it.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because the package explicitly requests org.apache.http.legacy
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because the package explicitly requests org.apache.http.legacy
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after, AndroidTestBaseUpdater::new);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
new file mode 100644
index 0000000..38755b9
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidTestRunnerSplitUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidTestRunnerSplitUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ @Test
+ public void android_test_runner_in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ANDROID_TEST_RUNNER)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ANDROID_TEST_MOCK)
+ .addUsesOptionalLibrary(ANDROID_TEST_RUNNER)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_RUNNER)
+ .addUsesOptionalLibrary(ANDROID_TEST_MOCK)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_RUNNER)
+ .addUsesOptionalLibrary(ANDROID_TEST_MOCK)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after, AndroidTestRunnerSplitUpdater::new);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
new file mode 100644
index 0000000..4c7899b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
+import android.content.pm.OptionalClassRunner;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link OrgApacheHttpLegacyUpdater}
+ */
+@SmallTest
+@RunWith(OptionalClassRunner.class)
+@OptionalClassRunner.OptionalClass("android.content.pm.parsing.library.OrgApacheHttpLegacyUpdater")
+public class OrgApacheHttpLegacyUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ private static final String OTHER_LIBRARY = "other.library";
+
+ @Test
+ public void targeted_at_O() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed();
+
+ // Should add org.apache.http.legacy.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_not_empty_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed();
+
+ // The org.apache.http.legacy jar should be added at the start of the list because it
+ // is not on the bootclasspath and the package targets pre-P.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because although org.apache.http.legacy has been removed from
+ // the bootclasspath the package explicitly requests it.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because although org.apache.http.legacy has been removed from
+ // the bootclasspath the package explicitly requests it.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because the package explicitly requests org.apache.http.legacy
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change is required because the package explicitly requests org.apache.http.legacy
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after, OrgApacheHttpLegacyUpdater::new);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java
new file mode 100644
index 0000000..00d468d
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.library;
+
+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.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdaterTest {
+
+ @Test
+ public void null_usesLibraries_and_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Detect when the android.test.base is not on the bootclasspath.
+ *
+ * <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
+ * succeed otherwise. This allows a developer to ensure that the tests are being run in the
+ * correct environment.
+ */
+ @Test
+ public void detectWhenATBisOnBCP() {
+ Assume.assumeTrue(PackageBackwardCompatibility.bootClassPathContainsATB());
+ }
+
+ /**
+ * Ensures that the {@link PackageBackwardCompatibility} uses {@link OrgApacheHttpLegacyUpdater}
+ * and {@link AndroidTestBaseUpdater} when necessary.
+ *
+ * <p>More comprehensive tests for that class can be found in
+ * {@link OrgApacheHttpLegacyUpdaterTest}.
+ */
+ @Test
+ public void targeted_at_O() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed();
+
+ ParsingPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .setTargetSdkVersion(Build.VERSION_CODES.O);
+
+ if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
+ after.addUsesLibrary(ANDROID_TEST_BASE);
+ }
+ after.addUsesLibrary(ORG_APACHE_HTTP_LEGACY);
+
+ checkBackwardsCompatibility(before, after.hideAsParsed().hideAsFinal());
+ }
+
+ /**
+ * Ensures that the {@link PackageBackwardCompatibility} uses
+ * {@link RemoveUnnecessaryAndroidTestBaseLibrary}
+ * when necessary.
+ *
+ * <p>More comprehensive tests for that class can be found in
+ * {@link RemoveUnnecessaryAndroidTestBaseLibraryTest}.
+ */
+ @Test
+ public void android_test_base_in_usesLibraries() {
+ Assume.assumeTrue("Test requires that "
+ + ANDROID_TEST_BASE + " is on the bootclasspath",
+ PackageBackwardCompatibility.bootClassPathContainsATB());
+
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ // android.test.base should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Ensures that the {@link PackageBackwardCompatibility} uses a
+ * {@link PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater}.
+ *
+ * <p>More comprehensive tests for that class can be found in
+ * {@link AndroidTestRunnerSplitUpdaterTest}.
+ */
+ @Test
+ public void android_test_runner_in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_RUNNER)
+ .hideAsParsed();
+
+ ParsingPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
+ after.addUsesLibrary(ANDROID_TEST_BASE);
+ }
+ after.addUsesLibrary(ANDROID_TEST_MOCK);
+ after.addUsesLibrary(ANDROID_TEST_RUNNER);
+
+ checkBackwardsCompatibility(before, after.hideAsParsed().hideAsFinal());
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
new file mode 100644
index 0000000..e7a80e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.library;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
+
+import java.util.function.Supplier;
+
+/**
+ * Helper for classes that test {@link PackageSharedLibraryUpdater}.
+ */
+abstract class PackageSharedLibraryUpdaterTest {
+
+ protected static final String PACKAGE_NAME = "org.package.name";
+
+ static void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+ Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
+ updaterSupplier.get().updatePackage(before);
+ check(before.hideAsFinal(), after);
+ }
+
+ private static void check(AndroidPackage before, AndroidPackage after) {
+ assertEquals("targetSdkVersion should not be changed",
+ after.getTargetSdkVersion(),
+ before.getTargetSdkVersion());
+ assertEquals("usesLibraries not updated correctly",
+ after.getUsesLibraries(),
+ before.getUsesLibraries());
+ assertEquals("usesOptionalLibraries not updated correctly",
+ after.getUsesOptionalLibraries(),
+ before.getUsesOptionalLibraries());
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
new file mode 100644
index 0000000..fd3ba2b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link RemoveUnnecessaryAndroidTestBaseLibrary}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class RemoveUnnecessaryAndroidTestBaseLibraryTest
+ extends PackageSharedLibraryUpdaterTest {
+
+ private static final String OTHER_LIBRARY = "other.library";
+
+ @Test
+ public void targeted_at_O() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change required.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_not_empty_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change required.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ // android.test.base should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ // android.test.base should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ // android.test.base should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ // android.test.base should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_bothLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ANDROID_TEST_BASE)
+ .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+ .hideAsParsed();
+
+ // android.test.base should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
+ // PackageBackwardCompatibility and that seems to create a package-private lambda in
+ // android.content.pm which this then tries to reuse but fails because it cannot access
+ // package-private classes/members because the test is loaded by a different ClassLoader
+ // than the lambda.
+ checkBackwardsCompatibility(before, after,
+ () -> new RemoveUnnecessaryAndroidTestBaseLibrary());
+ }
+
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
new file mode 100644
index 0000000..d3494d9
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link RemoveUnnecessaryOrgApacheHttpLegacyLibrary}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest
+ extends PackageSharedLibraryUpdaterTest {
+
+ private static final String OTHER_LIBRARY = "other.library";
+
+ @Test
+ public void targeted_at_O() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change required.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_not_empty_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed();
+
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(OTHER_LIBRARY)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ // No change required.
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ // org.apache.http.legacy should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ // org.apache.http.legacy should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ // org.apache.http.legacy should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ // org.apache.http.legacy should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_bothLibraries() {
+ ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+ .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+ .hideAsParsed();
+
+ // org.apache.http.legacy should be removed from the libraries because it is provided
+ // on the bootclasspath and providing both increases start up cost unnecessarily.
+ AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed()
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
+ // PackageBackwardCompatibility and that seems to create a package-private lambda in
+ // android.content.pm which this then tries to reuse but fails because it cannot access
+ // package-private classes/members because the test is loaded by a different ClassLoader
+ // than the lambda.
+ checkBackwardsCompatibility(before, after,
+ () -> new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
+ }
+}