diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2b266b730485f..c9846510be4ff 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1621,7 +1621,7 @@ public class PackageParser { } final AttributeSet attrs = parser; - return parseApkLite(apkPath, parser, attrs, signingDetails); + return parseApkLite(apkPath, parser, attrs, signingDetails, flags); } catch (XmlPullParserException | IOException | RuntimeException e) { Slog.w(TAG, "Failed to parse " + apkPath, e); @@ -1708,7 +1708,7 @@ public class PackageParser { } private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs, - SigningDetails signingDetails) + SigningDetails signingDetails, int flags) throws IOException, XmlPullParserException, PackageParserException { final Pair packageSplit = parsePackageSplitNames(parser, attrs); @@ -1716,11 +1716,12 @@ public class PackageParser { int versionCode = 0; int versionCodeMajor = 0; int revisionCode = 0; + int targetSdkVersion = 0; boolean coreApp = false; boolean debuggable = false; boolean multiArch = false; boolean use32bitAbi = false; - boolean extractNativeLibs = true; + Boolean extractNativeLibsProvided = null; boolean isolatedSplits = false; boolean isFeatureSplit = false; boolean isSplitRequired = false; @@ -1785,7 +1786,8 @@ public class PackageParser { use32bitAbi = attrs.getAttributeBooleanValue(i, false); } if ("extractNativeLibs".equals(attr)) { - extractNativeLibs = attrs.getAttributeBooleanValue(i, true); + extractNativeLibsProvided = Boolean.valueOf( + attrs.getAttributeBooleanValue(i, true)); } if ("preferCodeIntegrity".equals(attr)) { preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false); @@ -1803,9 +1805,51 @@ public class PackageParser { PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, " tag requires 'android:name' attribute"); } + } else if (TAG_USES_SDK.equals(parser.getName())) { + final String[] errorMsg = new String[1]; + Pair versions = deriveSdkVersions(new AbstractVersionsAccessor() { + @Override public String getMinSdkVersionCode() { + return getAttributeAsString("minSdkVersion"); + } + + @Override public int getMinSdkVersion() { + return getAttributeAsInt("minSdkVersion"); + } + + @Override public String getTargetSdkVersionCode() { + return getAttributeAsString("targetSdkVersion"); + } + + @Override public int getTargetSdkVersion() { + return getAttributeAsInt("targetSdkVersion"); + } + + private String getAttributeAsString(String name) { + return attrs.getAttributeValue(ANDROID_RESOURCES, name); + } + + private int getAttributeAsInt(String name) { + try { + return attrs.getAttributeIntValue(ANDROID_RESOURCES, name, -1); + } catch (NumberFormatException e) { + return -1; + } + } + }, flags, errorMsg); + + if (versions == null) { + throw new PackageParserException( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, errorMsg[0]); + } + + targetSdkVersion = versions.second; } } + final boolean extractNativeLibsDefault = targetSdkVersion < Build.VERSION_CODES.Q; + final boolean extractNativeLibs = (extractNativeLibsProvided != null) + ? extractNativeLibsProvided : extractNativeLibsDefault; + if (preferCodeIntegrity && extractNativeLibs) { throw new PackageParserException( PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, @@ -2215,65 +2259,60 @@ public class PackageParser { } else if (tagName.equals(TAG_USES_SDK)) { if (SDK_VERSION > 0) { - sa = res.obtainAttributes(parser, - com.android.internal.R.styleable.AndroidManifestUsesSdk); + sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk); + final TypedArray saFinal = sa; + Pair versions = deriveSdkVersions( + new AbstractVersionsAccessor() { + @Override public String getMinSdkVersionCode() { + return getAttributeAsString( + R.styleable.AndroidManifestUsesSdk_minSdkVersion); + } - int minVers = 1; - String minCode = null; - int targetVers = 0; - String targetCode = null; + @Override public int getMinSdkVersion() { + return getAttributeAsInt( + R.styleable.AndroidManifestUsesSdk_minSdkVersion); + } - TypedValue val = sa.peekValue( - com.android.internal.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; - } + @Override public String getTargetSdkVersionCode() { + return getAttributeAsString( + R.styleable.AndroidManifestUsesSdk_targetSdkVersion); + } + + @Override public int getTargetSdkVersion() { + return getAttributeAsInt( + R.styleable.AndroidManifestUsesSdk_targetSdkVersion); + } + + private String getAttributeAsString(int index) { + TypedValue val = saFinal.peekValue(index); + if (val != null && val.type == TypedValue.TYPE_STRING + && val.string != null) { + return val.string.toString(); + } + return null; + } + + private int getAttributeAsInt(int index) { + TypedValue val = saFinal.peekValue(index); + if (val != null && val.type != TypedValue.TYPE_STRING) { + // If it's not a string, it's an integer. + return val.data; + } + return -1; + } + }, flags, outError); + + if (versions == null) { + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; } - val = sa.peekValue( - com.android.internal.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; - } + pkg.applicationInfo.minSdkVersion = versions.first; + pkg.applicationInfo.targetSdkVersion = versions.second; sa.recycle(); - - final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode, - SDK_VERSION, SDK_CODENAMES, outError); - if (minSdkVersion < 0) { - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - - boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0; - final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers, - targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch); - if (targetSdkVersion < 0) { - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - - pkg.applicationInfo.minSdkVersion = minSdkVersion; - pkg.applicationInfo.targetSdkVersion = targetSdkVersion; } - XmlUtils.skipCurrentTag(parser); - } else if (tagName.equals(TAG_SUPPORT_SCREENS)) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestSupportsScreens); @@ -2675,6 +2714,67 @@ public class PackageParser { return -1; } + private interface AbstractVersionsAccessor { + /** Returns minimum SDK version code string, or null if absent. */ + String getMinSdkVersionCode(); + + /** Returns minimum SDK version code, or -1 if absent. */ + int getMinSdkVersion(); + + /** Returns target SDK version code string, or null if absent. */ + String getTargetSdkVersionCode(); + + /** Returns target SDK version code, or -1 if absent. */ + int getTargetSdkVersion(); + } + + private static @Nullable Pair deriveSdkVersions( + @NonNull AbstractVersionsAccessor accessor, int flags, String[] outError) { + int minVers = 1; + String minCode = null; + int targetVers = 0; + String targetCode = null; + + String code = accessor.getMinSdkVersionCode(); + int version = accessor.getMinSdkVersion(); + // Check integer first since code is almost never a null string (e.g. "28"). + if (version >= 0) { + minVers = version; + } else if (code != null) { + minCode = code; + } + + code = accessor.getTargetSdkVersionCode(); + version = accessor.getTargetSdkVersion(); + // Check integer first since code is almost never a null string (e.g. "28"). + if (version >= 0) { + targetVers = version; + } else if (code != null) { + targetCode = code; + if (minCode == null) { + minCode = targetCode; + } + } else { + targetVers = minVers; + targetCode = minCode; + } + + final int minSdkVersion = computeMinSdkVersion(minVers, minCode, + SDK_VERSION, SDK_CODENAMES, outError); + if (minSdkVersion < 0) { + return null; + } + + boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0; + final int targetSdkVersion = computeTargetSdkVersion(targetVers, + targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch); + if (targetSdkVersion < 0) { + return null; + } + + return Pair.create(minSdkVersion, targetSdkVersion); + } + /** * Computes the minSdkVersion to use at runtime. If the package is not * compatible with this platform, populates {@code outError[0]} with an