diff options
| author | 2016-12-19 22:23:09 +0000 | |
|---|---|---|
| committer | 2016-12-19 22:23:12 +0000 | |
| commit | ccec35c456f12a9438a524793159f5b2bda69443 (patch) | |
| tree | d9c6498d314e92476d84c30406961787b4a69621 | |
| parent | 4037fa05283f4474e2922e841d644dac852b0d8e (diff) | |
| parent | 82f4b263a09630b9937ecb2cc3dea5b21eaa7b8f (diff) | |
Merge "Add tests, refactor min/targetSdkVersion checks in PackageParser"
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 199 | ||||
| -rw-r--r-- | core/tests/coretests/README | 50 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/content/pm/PackageParserTest.java | 218 |
3 files changed, 413 insertions, 54 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 32bf66a92fd5..2236291fd248 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -24,7 +24,10 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; @@ -58,7 +61,6 @@ import android.util.jar.StrictJarFile; import android.view.Gravity; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -85,7 +87,6 @@ import java.util.zip.ZipEntry; import libcore.io.IoUtils; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE; import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; @@ -2103,63 +2104,22 @@ public class PackageParser { sa.recycle(); - if (minCode != null) { - boolean allowedCodename = false; - for (String codename : SDK_CODENAMES) { - if (minCode.equals(codename)) { - allowedCodename = true; - break; - } - } - if (!allowedCodename) { - if (SDK_CODENAMES.length > 0) { - outError[0] = "Requires development platform " + minCode - + " (current platform is any of " - + Arrays.toString(SDK_CODENAMES) + ")"; - } else { - outError[0] = "Requires development platform " + minCode - + " but this is a release platform."; - } - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - pkg.applicationInfo.minSdkVersion = - android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; - } else if (minVers > SDK_VERSION) { - outError[0] = "Requires newer sdk version #" + minVers - + " (current version is #" + SDK_VERSION + ")"; + final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode, + SDK_VERSION, SDK_CODENAMES, outError); + if (minSdkVersion < 0) { mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; - } else { - pkg.applicationInfo.minSdkVersion = minVers; } - if (targetCode != null) { - boolean allowedCodename = false; - for (String codename : SDK_CODENAMES) { - if (targetCode.equals(codename)) { - allowedCodename = true; - break; - } - } - if (!allowedCodename) { - if (SDK_CODENAMES.length > 0) { - outError[0] = "Requires development platform " + targetCode - + " (current platform is any of " - + Arrays.toString(SDK_CODENAMES) + ")"; - } else { - outError[0] = "Requires development platform " + targetCode - + " but this is a release platform."; - } - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - // If the code matches, it definitely targets this SDK. - pkg.applicationInfo.targetSdkVersion - = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; - } else { - pkg.applicationInfo.targetSdkVersion = targetVers; + final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers, + targetCode, SDK_VERSION, SDK_CODENAMES, outError); + if (targetSdkVersion < 0) { + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; } + + pkg.applicationInfo.minSdkVersion = minSdkVersion; + pkg.applicationInfo.targetSdkVersion = targetSdkVersion; } XmlUtils.skipCurrentTag(parser); @@ -2407,6 +2367,137 @@ public class PackageParser { return pkg; } + /** + * Computes the targetSdkVersion to use at runtime. If the package is not + * compatible with this platform, populates {@code outError[0]} with an + * error message. + * <p> + * 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: + * <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 + * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}. + * <li>If this is a released platform, this method will return -1 to + * indicate that the package is not compatible with this platform. + * </ul> + * + * @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 platformSdkVersion platform SDK version number, typically + * Build.VERSION.SDK_INT + * @param platformSdkCodenames array of allowed pre-release SDK codenames + * for this platform + * @param outError output array to populate with error, if applicable + * @return the targetSdkVersion to use at runtime, or -1 if the package is + * not compatible with this platform + * @hide Exposed for unit testing only. + */ + @TestApi + public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers, + @Nullable String targetCode, @IntRange(from = 1) int platformSdkVersion, + @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { + // If it's a release SDK, return the version number unmodified. + if (targetCode == null) { + return targetVers; + } + + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (ArrayUtils.contains(platformSdkCodenames, targetCode)) { + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + + // Otherwise, we're looking at an incompatible pre-release SDK. + if (platformSdkCodenames.length > 0) { + outError[0] = "Requires development platform " + targetCode + + " (current platform is any of " + + Arrays.toString(platformSdkCodenames) + ")"; + } else { + outError[0] = "Requires development platform " + targetCode + + " but this is a release platform."; + } + return -1; + } + + /** + * Computes the minSdkVersion to use at runtime. If the package is not + * compatible with this platform, populates {@code outError[0]} with an + * error message. + * <p> + * If {@code minCode} is not specified, e.g. the value is {@code null}, + * then behavior varies based on the {@code platformSdkVersion}: + * <ul> + * <li>If the platform SDK version is greater than or equal to the + * {@code minVers}, returns the {@code mniVers} unmodified. + * <li>Otherwise, returns -1 to indicate that the package is not + * compatible with this platform. + * </ul> + * <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: + * <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 + * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}. + * <li>If this is a released platform, this method will return -1 to + * indicate that the package is not compatible with this platform. + * </ul> + * + * @param minVers minSdkVersion number, if specified in the application + * manifest, or 1 otherwise + * @param minCode minSdkVersion code, if specified in the application + * manifest, or {@code null} otherwise + * @param platformSdkVersion platform SDK version number, typically + * Build.VERSION.SDK_INT + * @param platformSdkCodenames array of allowed prerelease SDK codenames + * for this platform + * @param outError output array to populate with error, if applicable + * @return the minSdkVersion to use at runtime, or -1 if the package is not + * compatible with this platform + * @hide Exposed for unit testing only. + */ + @TestApi + public static int computeMinSdkVersion(@IntRange(from = 1) int minVers, + @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, + @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { + // If it's a release SDK, make sure we meet the minimum SDK requirement. + if (minCode == null) { + if (minVers <= platformSdkVersion) { + return minVers; + } + + // We don't meet the minimum SDK requirement. + outError[0] = "Requires newer sdk version #" + minVers + + " (current version is #" + platformSdkVersion + ")"; + return -1; + } + + // If it's a pre-release SDK and the codename matches this platform, we + // definitely meet the minimum SDK requirement. + if (ArrayUtils.contains(platformSdkCodenames, minCode)) { + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + + // Otherwise, we're looking at an incompatible pre-release SDK. + if (platformSdkCodenames.length > 0) { + outError[0] = "Requires development platform " + minCode + + " (current platform is any of " + + Arrays.toString(platformSdkCodenames) + ")"; + } else { + outError[0] = "Requires development platform " + minCode + + " but this is a release platform."; + } + return -1; + } + private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) { FeatureInfo fi = new FeatureInfo(); TypedArray sa = res.obtainAttributes(attrs, diff --git a/core/tests/coretests/README b/core/tests/coretests/README new file mode 100644 index 000000000000..4a6984320e61 --- /dev/null +++ b/core/tests/coretests/README @@ -0,0 +1,50 @@ +* Copyright (C) 2016 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. + + +INTRODUCTION + +The Android platform core tests (APCT) consist of unit tests for core platform +functionality. These differ from CTS in that they are not necessarily testing +public APIs and are not guaranteed to work outside of AOSP builds. + + +INSTRUCTIONS + +To run a test or set of tests, first build the FrameworksCoreTests package: + + make FrameworksCoreTests + +Next, install the resulting APK and run tests as you would normal JUnit tests: + + adb install out/target/product/.../data/app/FrameworksCoreTests/FrameworksCoreTests.apk + adb shell am instrument -w \ + com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + +To run a tests within a specific package, add the following argument AFTER -w: + + -e package android.content.pm + +To run a specific test or method within a test: + + -e class android.content.pm.PackageParserTest + -e class android.content.pm.PackageParserTest#testComputeMinSdkVersion + +To run tests in debug mode: + + -e debug true + +For more arguments, see the guide to command=line testing: + + https://developer.android.com/studio/test/command-line.html diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java new file mode 100644 index 000000000000..2a3c22c64ec2 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 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 static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.os.Build; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PackageParserTest { + 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[] 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; + private static final int NEWER_VERSION = 30; + + private void verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename, + boolean isPlatformReleased, int expectedMinSdk) { + final String[] outError = new String[1]; + final int result = PackageParser.computeMinSdkVersion( + minSdkVersion, + minSdkCodename, + PLATFORM_VERSION, + isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE, + outError); + + assertEquals(result, expectedMinSdk); + + if (expectedMinSdk == -1) { + assertNotNull(outError[0]); + } else { + assertNull(outError[0]); + } + } + + @Test + public void testComputeMinSdkVersion_preReleasePlatform() { + // Do allow older release minSdkVersion on pre-release platform. + // APP: Released API 10 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION); + + // Do allow same release minSdkVersion on pre-release platform. + // APP: Released API 20 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION); + + // Don't allow newer release minSdkVersion on pre-release platform. + // APP: Released API 30 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, false, -1); + + // Don't allow older pre-release minSdkVersion on pre-release platform. + // APP: Pre-release API 10 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1); + + // Do allow same pre-release minSdkVersion on pre-release platform, + // but overwrite the specified version with CUR_DEVELOPMENT. + // APP: Pre-release API 20 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false, + Build.VERSION_CODES.CUR_DEVELOPMENT); + + // Don't allow newer pre-release minSdkVersion on pre-release platform. + // APP: Pre-release API 30 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1); + } + + @Test + public void testComputeMinSdkVersion_releasedPlatform() { + // Do allow older release minSdkVersion on released platform. + // APP: Released API 10 + // DEV: Released API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION); + + // Do allow same release minSdkVersion on released platform. + // APP: Released API 20 + // DEV: Released API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION); + + // Don't allow newer release minSdkVersion on released platform. + // APP: Released API 30 + // DEV: Released API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, true, -1); + + // Don't allow older pre-release minSdkVersion on released platform. + // APP: Pre-release API 10 + // DEV: Released API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1); + + // Don't allow same pre-release minSdkVersion on released platform. + // APP: Pre-release API 20 + // DEV: Released API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1); + + // Don't allow newer pre-release minSdkVersion on released platform. + // APP: Pre-release API 30 + // DEV: Released API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1); + } + + private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename, + boolean isPlatformReleased, int expectedTargetSdk) { + final String[] outError = new String[1]; + final int result = PackageParser.computeTargetSdkVersion( + targetSdkVersion, + targetSdkCodename, + PLATFORM_VERSION, + isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE, + outError); + + assertEquals(result, expectedTargetSdk); + + if (expectedTargetSdk == -1) { + assertNotNull(outError[0]); + } else { + assertNull(outError[0]); + } + } + + @Test + public void testComputeTargetSdkVersion_preReleasePlatform() { + // 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); + + // 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); + + // 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); + + // 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); + + // 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); + + // 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); + } + + @Test + public void testComputeTargetSdkVersion_releasedPlatform() { + // Do allow older release targetSdkVersion on released platform. + // APP: Released API 10 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, 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); + + // Do allow newer release targetSdkVersion on released platform. + // APP: Released API 30 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, 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); + + // 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); + + // 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); + } +} |