Loading core/java/com/android/server/SystemConfig.java +80 −7 Original line number Original line Diff line number Diff line Loading @@ -108,17 +108,74 @@ public class SystemConfig { public final String name; public final String name; public final String filename; public final String filename; public final String[] dependencies; public final String[] dependencies; /** * SDK version this library was added to the BOOTCLASSPATH. * * <p>At the SDK level specified in this field and higher, the apps' uses-library tags for * this library will be ignored, since the library is always available on BOOTCLASSPATH. * * <p>0 means not specified. */ public final int onBootclasspathSince; /** * SDK version this library was removed from the BOOTCLASSPATH. * * <p>At the SDK level specified in this field and higher, this library needs to be * explicitly added by apps. For compatibility reasons, when an app * targets an SDK less than the value of this attribute, this library is automatically * added. * * <p>0 means not specified. */ public final int onBootclasspathBefore; /** * Declares whether this library can be safely ignored from <uses-library> tags. * * <p> This can happen if the library initially had to be explicitly depended-on using that * tag but has since been moved to the BOOTCLASSPATH which means now is always available * and the tag is no longer required. */ public final boolean canBeSafelyIgnored; public final boolean isNative; public final boolean isNative; SharedLibraryEntry(String name, String filename, String[] dependencies) { this(name, filename, dependencies, false /* isNative */); @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { this(name, filename, dependencies, 0 /* onBootclasspathSince */, 0 /* onBootclasspathBefore */, isNative); } @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, int onBootclasspathSince, int onBootclassPathBefore) { this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore, false /* isNative */); } } SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { SharedLibraryEntry(String name, String filename, String[] dependencies, int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) { this.name = name; this.name = name; this.filename = filename; this.filename = filename; this.dependencies = dependencies; this.dependencies = dependencies; this.onBootclasspathSince = onBootclasspathSince; this.onBootclasspathBefore = onBootclasspathBefore; this.isNative = isNative; this.isNative = isNative; canBeSafelyIgnored = this.onBootclasspathSince != 0 && isSdkAtLeast(this.onBootclasspathSince); } private static boolean isSdkAtLeast(int level) { if ("REL".equals(Build.VERSION.CODENAME)) { return Build.VERSION.SDK_INT >= level; } return level == Build.VERSION_CODES.CUR_DEVELOPMENT || Build.VERSION.SDK_INT >= level; } } } } Loading Loading @@ -771,11 +828,17 @@ public class SystemConfig { XmlUtils.skipCurrentTag(parser); XmlUtils.skipCurrentTag(parser); } } } break; } break; case "updatable-library": // "updatable-library" is meant to behave exactly like "library" case "library": { case "library": { if (allowLibs) { if (allowLibs) { String lname = parser.getAttributeValue(null, "name"); String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); String lfile = parser.getAttributeValue(null, "file"); String ldependency = parser.getAttributeValue(null, "dependency"); String ldependency = parser.getAttributeValue(null, "dependency"); int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk", 0); int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk", 0); if (lname == null) { if (lname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); + parser.getPositionDescription()); Loading @@ -783,11 +846,21 @@ public class SystemConfig { Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " + parser.getPositionDescription()); + parser.getPositionDescription()); } else { } else { //Log.i(TAG, "Got library " + lname + " in " + lfile); boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT; boolean allowedMaxSdk = maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT; if (allowedMinSdk && allowedMaxSdk) { int bcpSince = XmlUtils.readIntAttribute(parser, "on-bootclasspath-since", 0); int bcpBefore = XmlUtils.readIntAttribute(parser, "on-bootclasspath-before", 0); SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, ldependency == null ? new String[0] : ldependency.split(":")); ldependency == null ? new String[0] : ldependency.split(":"), bcpSince, bcpBefore); mSharedLibraries.put(lname, entry); mSharedLibraries.put(lname, entry); } } } } else { } else { logNotAllowedInPartition(name, permFile, parser); logNotAllowedInPartition(name, permFile, parser); } } Loading services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java 0 → 100644 +67 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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.server.pm.parsing.library; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.ParsedPackage; /** * Updates packages to add or remove dependencies on shared libraries as per attributes * in the library declaration * * @hide */ @VisibleForTesting public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater { /** * ArrayMap like the one you find in {@link SystemConfig}. The keys are the library names. */ private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries; public ApexSharedLibraryUpdater( ArrayMap<String, SystemConfig.SharedLibraryEntry> sharedLibraries) { mSharedLibraries = sharedLibraries; } @Override public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) { final int builtInLibCount = mSharedLibraries.size(); for (int i = 0; i < builtInLibCount; i++) { updateSharedLibraryForPackage(mSharedLibraries.valueAt(i), parsedPackage); } } private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry, ParsedPackage parsedPackage) { if (entry.onBootclasspathBefore != 0 && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) { // this package targets an API where this library was in the BCP, so add // the library transparently in case the package is using it prefixRequiredLibrary(parsedPackage, entry.name); } if (entry.canBeSafelyIgnored) { // the library is now present in the BCP and always available; we don't need to add // it a second time removeLibrary(parsedPackage, entry.name); } } } services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.pm.parsing.ParsingPackage; import android.util.Log; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.pkg.ParsedPackage; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -63,6 +64,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters); boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters); // ApexSharedLibraryUpdater should be the last one, to allow modifications introduced by // mainline after dessert release. packageUpdaters.add(new ApexSharedLibraryUpdater( SystemConfig.getInstance().getSharedLibraries())); PackageSharedLibraryUpdater[] updaterArray = packageUpdaters PackageSharedLibraryUpdater[] updaterArray = packageUpdaters .toArray(new PackageSharedLibraryUpdater[0]); .toArray(new PackageSharedLibraryUpdater[0]); INSTANCE = new PackageBackwardCompatibility( INSTANCE = new PackageBackwardCompatibility( Loading Loading @@ -106,6 +112,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { private final PackageSharedLibraryUpdater[] mPackageUpdaters; private final PackageSharedLibraryUpdater[] mPackageUpdaters; @VisibleForTesting PackageSharedLibraryUpdater[] getPackageUpdaters() { return mPackageUpdaters; } private PackageBackwardCompatibility( private PackageBackwardCompatibility( boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) { boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) { this.mBootClassPathContainsATB = bootClassPathContainsATB; this.mBootClassPathContainsATB = bootClassPathContainsATB; Loading services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java 0 → 100644 +281 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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.server.pm.parsing.library; import android.os.Build; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import androidx.test.filters.SmallTest; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Test for {@link ApexSharedLibraryUpdater} */ @Presubmit @SmallTest @RunWith(JUnit4.class) public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest { private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries = new ArrayMap<>(8); @Before public void setUp() throws Exception { installSharedLibraries(); } private void installSharedLibraries() throws Exception { mSharedLibraries.clear(); insertLibrary("foo", 0, 0); insertLibrary("fooBcpSince30", 30, 0); insertLibrary("fooBcpBefore30", 0, 30); insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0); } private void insertLibrary(String libraryName, int onBootclasspathSince, int onBootclasspathBefore) { mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry( libraryName, "foo.jar", new String[0] /* dependencies */, onBootclasspathSince, onBootclasspathBefore ) ); } @Test public void testRegularAppOnRPlus() { // platform Q should have changes (tested below) // these should have no changes checkNoChanges(Build.VERSION_CODES.R); checkNoChanges(Build.VERSION_CODES.S); checkNoChanges(Build.VERSION_CODES.TIRAMISU); checkNoChanges(Build.VERSION_CODES.CUR_DEVELOPMENT); } private void checkNoChanges(int targetSdkVersion) { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(targetSdkVersion) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(targetSdkVersion) .hideAsParsed()) .hideAsFinal(); checkBackwardsCompatibility(before, after); } @Test public void testBcpSince30Applied() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooBcpSince30") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // note: target sdk is not what matters in this logic. It's the system SDK // should be removed because on 30+ (R+) it is implicit checkBackwardsCompatibility(before, after); } @Test public void testBcpSince11kNotAppliedWithoutLibrary() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // note: target sdk is not what matters in this logic. It's the system SDK // nothing should change because the implicit from is only from a future platform release checkBackwardsCompatibility(before, after); } @Test public void testBcpSince11kNotAppliedWithLibrary() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooFromFuture") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooFromFuture") .hideAsParsed()) .hideAsFinal(); // note: target sdk is not what matters in this logic. It's the system SDK // nothing should change because the implicit from is only from a future platform release checkBackwardsCompatibility(before, after); } @Test public void testBcpBefore30NotApplied() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // should not be affected because it is still in the BCP in 30 / R checkBackwardsCompatibility(before, after); } @Test public void testBcpBefore30Applied() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .addUsesLibrary("fooBcpBefore30") .hideAsParsed()) .hideAsFinal(); // should be present because this was in BCP in 29 / Q checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. All of this happening before the current * SDK. */ @Test public void testBcpRemovedThenAddedPast() { insertLibrary("fooBcpRemovedThenAdded", 30, 28); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.N) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.N) .addUsesLibrary("fooBcpBefore30") .hideAsParsed()) .hideAsFinal(); // the library is now in the BOOTCLASSPATH (for the second time) so it doesn't need to be // listed checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. The first part happening before the * current SDK and the second part after. */ @Test public void testBcpRemovedThenAddedMiddle_targetQ() { insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .addUsesLibrary("fooBcpRemovedThenAdded") .addUsesLibrary("fooBcpBefore30") .hideAsParsed()) .hideAsFinal(); // in this example, we are at the point where the library is not in the BOOTCLASSPATH. // Because the app targets Q / 29 (when this library was in the BCP) then we need to add it checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. The first part happening before the * current SDK and the second part after. */ @Test public void testBcpRemovedThenAddedMiddle_targetR() { insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // in this example, we are at the point where the library is not in the BOOTCLASSPATH. // Because the app targets R/30 (when this library was removed from the BCP) then we don't //need to add it checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. The first part happening before the * current SDK and the second part after. */ @Test public void testBcpRemovedThenAddedMiddle_targetR_usingLib() { insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooBcpRemovedThenAdded") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooBcpRemovedThenAdded") .hideAsParsed()) .hideAsFinal(); // in this example, we are at the point where the library is not in the BOOTCLASSPATH. // Because the app wants to use the library, it needs to be present checkBackwardsCompatibility(before, after); } private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, () -> new ApexSharedLibraryUpdater(mSharedLibraries)); } } services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_T import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import static com.google.common.truth.Truth.assertThat; import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.ParsingPackage; import android.os.Build; import android.os.Build; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit; Loading Loading @@ -182,6 +184,22 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); } } /** * Ensures that ApexSharedLibraryUpdater is the last updater in the list of package updaters * used by PackageBackwardCompatibility. * * This is required so mainline can add and remove libraries installed by the platform updaters. */ @Test public void testApexPackageUpdaterOrdering() { PackageBackwardCompatibility instance = (PackageBackwardCompatibility) PackageBackwardCompatibility.getInstance(); PackageSharedLibraryUpdater[] updaterArray = instance.getPackageUpdaters(); PackageSharedLibraryUpdater lastUpdater = updaterArray[updaterArray.length - 1]; assertThat(lastUpdater).isInstanceOf(ApexSharedLibraryUpdater.class); } private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); } } Loading Loading
core/java/com/android/server/SystemConfig.java +80 −7 Original line number Original line Diff line number Diff line Loading @@ -108,17 +108,74 @@ public class SystemConfig { public final String name; public final String name; public final String filename; public final String filename; public final String[] dependencies; public final String[] dependencies; /** * SDK version this library was added to the BOOTCLASSPATH. * * <p>At the SDK level specified in this field and higher, the apps' uses-library tags for * this library will be ignored, since the library is always available on BOOTCLASSPATH. * * <p>0 means not specified. */ public final int onBootclasspathSince; /** * SDK version this library was removed from the BOOTCLASSPATH. * * <p>At the SDK level specified in this field and higher, this library needs to be * explicitly added by apps. For compatibility reasons, when an app * targets an SDK less than the value of this attribute, this library is automatically * added. * * <p>0 means not specified. */ public final int onBootclasspathBefore; /** * Declares whether this library can be safely ignored from <uses-library> tags. * * <p> This can happen if the library initially had to be explicitly depended-on using that * tag but has since been moved to the BOOTCLASSPATH which means now is always available * and the tag is no longer required. */ public final boolean canBeSafelyIgnored; public final boolean isNative; public final boolean isNative; SharedLibraryEntry(String name, String filename, String[] dependencies) { this(name, filename, dependencies, false /* isNative */); @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { this(name, filename, dependencies, 0 /* onBootclasspathSince */, 0 /* onBootclasspathBefore */, isNative); } @VisibleForTesting public SharedLibraryEntry(String name, String filename, String[] dependencies, int onBootclasspathSince, int onBootclassPathBefore) { this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore, false /* isNative */); } } SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { SharedLibraryEntry(String name, String filename, String[] dependencies, int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) { this.name = name; this.name = name; this.filename = filename; this.filename = filename; this.dependencies = dependencies; this.dependencies = dependencies; this.onBootclasspathSince = onBootclasspathSince; this.onBootclasspathBefore = onBootclasspathBefore; this.isNative = isNative; this.isNative = isNative; canBeSafelyIgnored = this.onBootclasspathSince != 0 && isSdkAtLeast(this.onBootclasspathSince); } private static boolean isSdkAtLeast(int level) { if ("REL".equals(Build.VERSION.CODENAME)) { return Build.VERSION.SDK_INT >= level; } return level == Build.VERSION_CODES.CUR_DEVELOPMENT || Build.VERSION.SDK_INT >= level; } } } } Loading Loading @@ -771,11 +828,17 @@ public class SystemConfig { XmlUtils.skipCurrentTag(parser); XmlUtils.skipCurrentTag(parser); } } } break; } break; case "updatable-library": // "updatable-library" is meant to behave exactly like "library" case "library": { case "library": { if (allowLibs) { if (allowLibs) { String lname = parser.getAttributeValue(null, "name"); String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); String lfile = parser.getAttributeValue(null, "file"); String ldependency = parser.getAttributeValue(null, "dependency"); String ldependency = parser.getAttributeValue(null, "dependency"); int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk", 0); int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk", 0); if (lname == null) { if (lname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); + parser.getPositionDescription()); Loading @@ -783,11 +846,21 @@ public class SystemConfig { Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " + parser.getPositionDescription()); + parser.getPositionDescription()); } else { } else { //Log.i(TAG, "Got library " + lname + " in " + lfile); boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT; boolean allowedMaxSdk = maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT; if (allowedMinSdk && allowedMaxSdk) { int bcpSince = XmlUtils.readIntAttribute(parser, "on-bootclasspath-since", 0); int bcpBefore = XmlUtils.readIntAttribute(parser, "on-bootclasspath-before", 0); SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, ldependency == null ? new String[0] : ldependency.split(":")); ldependency == null ? new String[0] : ldependency.split(":"), bcpSince, bcpBefore); mSharedLibraries.put(lname, entry); mSharedLibraries.put(lname, entry); } } } } else { } else { logNotAllowedInPartition(name, permFile, parser); logNotAllowedInPartition(name, permFile, parser); } } Loading
services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java 0 → 100644 +67 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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.server.pm.parsing.library; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.ParsedPackage; /** * Updates packages to add or remove dependencies on shared libraries as per attributes * in the library declaration * * @hide */ @VisibleForTesting public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater { /** * ArrayMap like the one you find in {@link SystemConfig}. The keys are the library names. */ private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries; public ApexSharedLibraryUpdater( ArrayMap<String, SystemConfig.SharedLibraryEntry> sharedLibraries) { mSharedLibraries = sharedLibraries; } @Override public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) { final int builtInLibCount = mSharedLibraries.size(); for (int i = 0; i < builtInLibCount; i++) { updateSharedLibraryForPackage(mSharedLibraries.valueAt(i), parsedPackage); } } private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry, ParsedPackage parsedPackage) { if (entry.onBootclasspathBefore != 0 && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) { // this package targets an API where this library was in the BCP, so add // the library transparently in case the package is using it prefixRequiredLibrary(parsedPackage, entry.name); } if (entry.canBeSafelyIgnored) { // the library is now present in the BCP and always available; we don't need to add // it a second time removeLibrary(parsedPackage, entry.name); } } }
services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.pm.parsing.ParsingPackage; import android.util.Log; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.pkg.ParsedPackage; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -63,6 +64,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters); boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters); // ApexSharedLibraryUpdater should be the last one, to allow modifications introduced by // mainline after dessert release. packageUpdaters.add(new ApexSharedLibraryUpdater( SystemConfig.getInstance().getSharedLibraries())); PackageSharedLibraryUpdater[] updaterArray = packageUpdaters PackageSharedLibraryUpdater[] updaterArray = packageUpdaters .toArray(new PackageSharedLibraryUpdater[0]); .toArray(new PackageSharedLibraryUpdater[0]); INSTANCE = new PackageBackwardCompatibility( INSTANCE = new PackageBackwardCompatibility( Loading Loading @@ -106,6 +112,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { private final PackageSharedLibraryUpdater[] mPackageUpdaters; private final PackageSharedLibraryUpdater[] mPackageUpdaters; @VisibleForTesting PackageSharedLibraryUpdater[] getPackageUpdaters() { return mPackageUpdaters; } private PackageBackwardCompatibility( private PackageBackwardCompatibility( boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) { boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) { this.mBootClassPathContainsATB = bootClassPathContainsATB; this.mBootClassPathContainsATB = bootClassPathContainsATB; Loading
services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java 0 → 100644 +281 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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.server.pm.parsing.library; import android.os.Build; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import androidx.test.filters.SmallTest; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Test for {@link ApexSharedLibraryUpdater} */ @Presubmit @SmallTest @RunWith(JUnit4.class) public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest { private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries = new ArrayMap<>(8); @Before public void setUp() throws Exception { installSharedLibraries(); } private void installSharedLibraries() throws Exception { mSharedLibraries.clear(); insertLibrary("foo", 0, 0); insertLibrary("fooBcpSince30", 30, 0); insertLibrary("fooBcpBefore30", 0, 30); insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0); } private void insertLibrary(String libraryName, int onBootclasspathSince, int onBootclasspathBefore) { mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry( libraryName, "foo.jar", new String[0] /* dependencies */, onBootclasspathSince, onBootclasspathBefore ) ); } @Test public void testRegularAppOnRPlus() { // platform Q should have changes (tested below) // these should have no changes checkNoChanges(Build.VERSION_CODES.R); checkNoChanges(Build.VERSION_CODES.S); checkNoChanges(Build.VERSION_CODES.TIRAMISU); checkNoChanges(Build.VERSION_CODES.CUR_DEVELOPMENT); } private void checkNoChanges(int targetSdkVersion) { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(targetSdkVersion) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(targetSdkVersion) .hideAsParsed()) .hideAsFinal(); checkBackwardsCompatibility(before, after); } @Test public void testBcpSince30Applied() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooBcpSince30") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // note: target sdk is not what matters in this logic. It's the system SDK // should be removed because on 30+ (R+) it is implicit checkBackwardsCompatibility(before, after); } @Test public void testBcpSince11kNotAppliedWithoutLibrary() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // note: target sdk is not what matters in this logic. It's the system SDK // nothing should change because the implicit from is only from a future platform release checkBackwardsCompatibility(before, after); } @Test public void testBcpSince11kNotAppliedWithLibrary() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooFromFuture") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooFromFuture") .hideAsParsed()) .hideAsFinal(); // note: target sdk is not what matters in this logic. It's the system SDK // nothing should change because the implicit from is only from a future platform release checkBackwardsCompatibility(before, after); } @Test public void testBcpBefore30NotApplied() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // should not be affected because it is still in the BCP in 30 / R checkBackwardsCompatibility(before, after); } @Test public void testBcpBefore30Applied() { ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .addUsesLibrary("fooBcpBefore30") .hideAsParsed()) .hideAsFinal(); // should be present because this was in BCP in 29 / Q checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. All of this happening before the current * SDK. */ @Test public void testBcpRemovedThenAddedPast() { insertLibrary("fooBcpRemovedThenAdded", 30, 28); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.N) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.N) .addUsesLibrary("fooBcpBefore30") .hideAsParsed()) .hideAsFinal(); // the library is now in the BOOTCLASSPATH (for the second time) so it doesn't need to be // listed checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. The first part happening before the * current SDK and the second part after. */ @Test public void testBcpRemovedThenAddedMiddle_targetQ() { insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.Q) .addUsesLibrary("fooBcpRemovedThenAdded") .addUsesLibrary("fooBcpBefore30") .hideAsParsed()) .hideAsFinal(); // in this example, we are at the point where the library is not in the BOOTCLASSPATH. // Because the app targets Q / 29 (when this library was in the BCP) then we need to add it checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. The first part happening before the * current SDK and the second part after. */ @Test public void testBcpRemovedThenAddedMiddle_targetR() { insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .hideAsParsed()) .hideAsFinal(); // in this example, we are at the point where the library is not in the BOOTCLASSPATH. // Because the app targets R/30 (when this library was removed from the BCP) then we don't //need to add it checkBackwardsCompatibility(before, after); } /** * Test a library that was first removed from the BCP [to a mainline module] and later was * moved back to the BCP via a mainline module update. The first part happening before the * current SDK and the second part after. */ @Test public void testBcpRemovedThenAddedMiddle_targetR_usingLib() { insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooBcpRemovedThenAdded") .hideAsParsed()); AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) .setTargetSdkVersion(Build.VERSION_CODES.R) .addUsesLibrary("fooBcpRemovedThenAdded") .hideAsParsed()) .hideAsFinal(); // in this example, we are at the point where the library is not in the BOOTCLASSPATH. // Because the app wants to use the library, it needs to be present checkBackwardsCompatibility(before, after); } private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, () -> new ApexSharedLibraryUpdater(mSharedLibraries)); } }
services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_T import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import static com.google.common.truth.Truth.assertThat; import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.ParsingPackage; import android.os.Build; import android.os.Build; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit; Loading Loading @@ -182,6 +184,22 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); } } /** * Ensures that ApexSharedLibraryUpdater is the last updater in the list of package updaters * used by PackageBackwardCompatibility. * * This is required so mainline can add and remove libraries installed by the platform updaters. */ @Test public void testApexPackageUpdaterOrdering() { PackageBackwardCompatibility instance = (PackageBackwardCompatibility) PackageBackwardCompatibility.getInstance(); PackageSharedLibraryUpdater[] updaterArray = instance.getPackageUpdaters(); PackageSharedLibraryUpdater lastUpdater = updaterArray[updaterArray.length - 1]; assertThat(lastUpdater).isInstanceOf(ApexSharedLibraryUpdater.class); } private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); } } Loading