Allow unknown codenames in target sdk version for APK-in-apexes

This is currently a recurring issue every new Android release as a few of
our apps will be targeting new platforms and they also get installed on
devices running older SDKs.

This change only changes behaviour for APK in apexes.

Test: atest com.android.server.pm.parsing.PackageParserLegacyCoreTest

Bug: 208239394

Merged-In: If1195c8ec39ac88a605e25dfb1c78d49a5aa3e7c
Change-Id: If1195c8ec39ac88a605e25dfb1c78d49a5aa3e7c
This commit is contained in:
Pedro Loureiro 2022-03-17 12:41:31 +00:00
parent 5ff11183cc
commit 5ef7d7b777
7 changed files with 105 additions and 45 deletions

View File

@ -326,6 +326,7 @@ java_defaults {
"av-types-aidl-java",
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-build",
"modules-utils-preconditions",
"modules-utils-synchronous-result-receiver",
"modules-utils-os",

View File

@ -568,7 +568,8 @@ public class ApkLiteParseUtils {
}
ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion(
targetVer, targetCode, SDK_CODENAMES, input);
targetVer, targetCode, SDK_CODENAMES, input,
/* allowUnknownCodenames= */ false);
if (targetResult.isError()) {
return input.error(targetResult);
}

View File

@ -36,6 +36,7 @@ import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.util.ArrayUtils;
import com.android.modules.utils.build.UnboundedSdkLevel;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
@ -334,8 +335,9 @@ public class FrameworkParsingPackageUtils {
* If {@code targetCode} is not specified, e.g. the value is {@code null}, then the {@code
* targetVers} will be returned unmodified.
* <p>
* Otherwise, the behavior varies based on whether the current platform is a pre-release
* version, e.g. the {@code platformSdkCodenames} array has length > 0:
* When {@code allowUnknownCodenames} is false, the behavior varies based on whether the
* current platform is a pre-release version, e.g. the {@code platformSdkCodenames} array has
* length > 0:
* <ul>
* <li>If this is a pre-release platform and the value specified by
* {@code targetCode} is contained within the array of allowed pre-release
@ -343,22 +345,32 @@ public class FrameworkParsingPackageUtils {
* <li>If this is a released platform, this method will return -1 to
* indicate that the package is not compatible with this platform.
* </ul>
* <p>
* When {@code allowUnknownCodenames} is true, any codename that is not known (presumed to be
* a codename announced after the build of the current device) is allowed and this method will
* return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
*
* @param targetVers targetSdkVersion number, if specified in the application
* manifest, or 0 otherwise
* @param targetCode targetSdkVersion code, if specified in the application manifest,
* or {@code null} otherwise
* @param platformSdkCodenames array of allowed pre-release SDK codenames for this platform
* @param targetVers targetSdkVersion number, if specified in the application
* manifest, or 0 otherwise
* @param targetCode targetSdkVersion code, if specified in the application manifest,
* or {@code null} otherwise
* @param platformSdkCodenames array of allowed pre-release SDK codenames for this platform
* @param allowUnknownCodenames allow unknown codenames, if true this method will accept unknown
* (presumed to be future) codenames
* @return the targetSdkVersion to use at runtime if successful
*/
public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
@Nullable String targetCode, @NonNull String[] platformSdkCodenames,
@NonNull ParseInput input) {
@NonNull ParseInput input, boolean allowUnknownCodenames) {
// If it's a release SDK, return the version number unmodified.
if (targetCode == null) {
return input.success(targetVers);
}
if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
// If it's a pre-release SDK and the codename matches this platform, it
// definitely targets this SDK.
if (matchTargetCode(platformSdkCodenames, targetCode)) {

View File

@ -5,3 +5,6 @@ rule android.hidl.** android.internal.hidl.@1
# Framework-specific renames.
rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
rule com.android.server.vcn.util.** com.android.server.vcn.repackaged.util.@1
# for modules-utils-build dependency
rule com.android.modules.utils.build.** android.internal.modules.utils.build.@1

View File

@ -30,7 +30,7 @@ import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_CHECK_MAX_SDK_VERSION;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
import android.annotation.NonNull;
import android.annotation.Nullable;
@ -359,7 +359,7 @@ final class InitAppsHelper {
try {
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_CHECK_MAX_SDK_VERSION;
parseFlags |= PARSE_APK_IN_APEX;
}
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
scanFlags, packageParser, executorService);

View File

@ -209,8 +209,6 @@ public class ParsingPackageUtils {
public static final int SDK_VERSION = Build.VERSION.SDK_INT;
public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
public static final String[] PREVIOUS_CODENAMES =
Build.VERSION.KNOWN_CODENAMES.toArray(new String[]{});
public static boolean sCompatibilityModeEnabled = true;
public static boolean sUseRoundIcon = false;
@ -238,7 +236,7 @@ public class ParsingPackageUtils {
*/
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
public static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
public static final int PARSE_CHECK_MAX_SDK_VERSION = 1 << 9;
public static final int PARSE_APK_IN_APEX = 1 << 9;
public static final int PARSE_CHATTY = 1 << 31;
@ -1534,7 +1532,7 @@ public class ParsingPackageUtils {
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws IOException, XmlPullParserException {
if (SDK_VERSION > 0) {
final boolean checkMaxSdkVersion = (flags & PARSE_CHECK_MAX_SDK_VERSION) != 0;
final boolean isApkInApex = (flags & PARSE_APK_IN_APEX) != 0;
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
try {
int minVers = ParsingUtils.DEFAULT_MIN_SDK_VERSION;
@ -1569,7 +1567,7 @@ public class ParsingPackageUtils {
targetCode = minCode;
}
if (checkMaxSdkVersion) {
if (isApkInApex) {
val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_maxSdkVersion);
if (val != null) {
// maxSdkVersion only supports integer
@ -1578,7 +1576,8 @@ public class ParsingPackageUtils {
}
ParseResult<Integer> targetSdkVersionResult = FrameworkParsingPackageUtils
.computeTargetSdkVersion(targetVers, targetCode, SDK_CODENAMES, input);
.computeTargetSdkVersion(targetVers, targetCode, SDK_CODENAMES, input,
isApkInApex);
if (targetSdkVersionResult.isError()) {
return input.error(targetSdkVersionResult);
}
@ -1601,7 +1600,7 @@ public class ParsingPackageUtils {
pkg.setMinSdkVersion(minSdkVersion)
.setTargetSdkVersion(targetSdkVersion);
if (checkMaxSdkVersion) {
if (isApkInApex) {
ParseResult<Integer> maxSdkVersionResult = FrameworkParsingPackageUtils
.computeMaxSdkVersion(maxVers, SDK_VERSION, input);
if (maxSdkVersionResult.isError()) {

View File

@ -84,15 +84,15 @@ import java.util.function.Function;
@RunWith(AndroidJUnit4.class)
public class PackageParserLegacyCoreTest {
private static final String RELEASED = null;
private static final String OLDER_PRE_RELEASE = "A";
private static final String PRE_RELEASE = "B";
private static final String NEWER_PRE_RELEASE = "C";
private static final String OLDER_PRE_RELEASE = "Q";
private static final String PRE_RELEASE = "R";
private static final String NEWER_PRE_RELEASE = "Z";
// Codenames with a fingerprint attached to them. These may only be present in the apps
// declared min SDK and not as platform codenames.
private static final String OLDER_PRE_RELEASE_WITH_FINGERPRINT = "A.fingerprint";
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 OLDER_PRE_RELEASE_WITH_FINGERPRINT = "Q.fingerprint";
private static final String PRE_RELEASE_WITH_FINGERPRINT = "R.fingerprint";
private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "Z.fingerprint";
private static final String[] CODENAMES_RELEASED = { /* empty */};
private static final String[] CODENAMES_PRE_RELEASE = {PRE_RELEASE};
@ -199,13 +199,14 @@ public class PackageParserLegacyCoreTest {
}
private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
boolean isPlatformReleased, int expectedTargetSdk) {
boolean isPlatformReleased, boolean allowUnknownCodenames, int expectedTargetSdk) {
final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat();
final ParseResult<Integer> result = FrameworkParsingPackageUtils.computeTargetSdkVersion(
targetSdkVersion,
targetSdkCodename,
isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
input);
input,
allowUnknownCodenames);
if (expectedTargetSdk == -1) {
assertTrue(result.isError());
@ -220,40 +221,61 @@ public class PackageParserLegacyCoreTest {
// Do allow older release targetSdkVersion on pre-release platform.
// APP: Released API 10
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION);
verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, false, OLDER_VERSION);
// Do allow same release targetSdkVersion on pre-release platform.
// APP: Released API 20
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, false, PLATFORM_VERSION);
// Do allow newer release targetSdkVersion on pre-release platform.
// APP: Released API 30
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION);
verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, false, NEWER_VERSION);
// Don't allow older pre-release targetSdkVersion on pre-release platform.
// APP: Pre-release API 10
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, false, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false,
false, -1
);
// Don't allow older pre-release targetSdkVersion on pre-release platform when
// allowUnknownCodenames is true.
// APP: Pre-release API 10
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false,
true, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false,
true, -1);
// Do allow same pre-release targetSdkVersion on pre-release platform,
// but overwrite the specified version with CUR_DEVELOPMENT.
// APP: Pre-release API 20
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
Build.VERSION_CODES.CUR_DEVELOPMENT);
false, Build.VERSION_CODES.CUR_DEVELOPMENT);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
Build.VERSION_CODES.CUR_DEVELOPMENT);
false, Build.VERSION_CODES.CUR_DEVELOPMENT);
// Don't allow newer pre-release targetSdkVersion on pre-release platform.
// APP: Pre-release API 30
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, false, -1);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false,
false, -1
);
// Do allow newer pre-release targetSdkVersion on pre-release platform when
// allowUnknownCodenames is true.
// APP: Pre-release API 30
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false,
true, Build.VERSION_CODES.CUR_DEVELOPMENT);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false,
true, Build.VERSION_CODES.CUR_DEVELOPMENT);
}
@Test
@ -261,36 +283,58 @@ public class PackageParserLegacyCoreTest {
// Do allow older release targetSdkVersion on released platform.
// APP: Released API 10
// DEV: Released API 20
verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION);
verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, false, OLDER_VERSION);
// Do allow same release targetSdkVersion on released platform.
// APP: Released API 20
// DEV: Released API 20
verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, false, PLATFORM_VERSION);
// Do allow newer release targetSdkVersion on released platform.
// APP: Released API 30
// DEV: Released API 20
verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION);
verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, false, NEWER_VERSION);
// Don't allow older pre-release targetSdkVersion on released platform.
// APP: Pre-release API 10
// DEV: Released API 20
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, false, -1);
verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true,
false, -1
);
// Don't allow same pre-release targetSdkVersion on released platform.
// APP: Pre-release API 20
// DEV: Released API 20
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, false, -1);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, false,
-1
);
// Don't allow same pre-release targetSdkVersion on released platform when
// allowUnknownCodenames is true.
// APP: Pre-release API 20
// DEV: Released API 20
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, true,
-1);
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, true,
-1);
// Don't allow newer pre-release targetSdkVersion on released platform.
// APP: Pre-release API 30
// DEV: Released API 20
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, false, -1);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true,
false, -1
);
// Do allow newer pre-release targetSdkVersion on released platform when
// allowUnknownCodenames is true.
// APP: Pre-release API 30
// DEV: Released API 20
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, true,
Build.VERSION_CODES.CUR_DEVELOPMENT);
verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true,
true, Build.VERSION_CODES.CUR_DEVELOPMENT);
}
/**