[automerge] Use strings in shared library updatability attributes 2p: a40064f2a7

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17201283

Bug: 191978330
Change-Id: Ie1fa6d6efaabb0f2db5c4b091223c79ea4ce2d1e
This commit is contained in:
Presubmit Automerger Backend
2022-04-06 12:53:32 +00:00
4 changed files with 193 additions and 55 deletions

View File

@@ -46,6 +46,7 @@ import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.build.UnboundedSdkLevel;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -126,7 +127,7 @@ public class SystemConfig {
*
* <p>0 means not specified.
*/
public final int onBootclasspathSince;
public final String onBootclasspathSince;
/**
* SDK version this library was removed from the BOOTCLASSPATH.
@@ -138,7 +139,7 @@ public class SystemConfig {
*
* <p>0 means not specified.
*/
public final int onBootclasspathBefore;
public final String onBootclasspathBefore;
/**
* Declares whether this library can be safely ignored from <uses-library> tags.
@@ -155,19 +156,19 @@ public class SystemConfig {
@VisibleForTesting
public SharedLibraryEntry(String name, String filename, String[] dependencies,
boolean isNative) {
this(name, filename, dependencies, 0 /* onBootclasspathSince */,
0 /* onBootclasspathBefore */, isNative);
this(name, filename, dependencies, null /* onBootclasspathSince */,
null /* onBootclasspathBefore */, isNative);
}
@VisibleForTesting
public SharedLibraryEntry(String name, String filename, String[] dependencies,
int onBootclasspathSince, int onBootclassPathBefore) {
this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore,
String onBootclasspathSince, String onBootclasspathBefore) {
this(name, filename, dependencies, onBootclasspathSince, onBootclasspathBefore,
false /* isNative */);
}
SharedLibraryEntry(String name, String filename, String[] dependencies,
int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) {
String onBootclasspathSince, String onBootclasspathBefore, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
@@ -175,16 +176,14 @@ public class SystemConfig {
this.onBootclasspathBefore = onBootclasspathBefore;
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;
// this entry can be ignored if either:
// - onBootclasspathSince is set and we are at or past that SDK
// - onBootclasspathBefore is set and we are before that SDK
canBeSafelyIgnored =
(this.onBootclasspathSince != null
&& UnboundedSdkLevel.isAtLeast(this.onBootclasspathSince))
|| (this.onBootclasspathBefore != null
&& !UnboundedSdkLevel.isAtLeast(this.onBootclasspathBefore));
}
}
@@ -878,10 +877,8 @@ public class SystemConfig {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
String ldependency = parser.getAttributeValue(null, "dependency");
int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk",
0);
int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk",
0);
String minDeviceSdk = parser.getAttributeValue(null, "min-device-sdk");
String maxDeviceSdk = parser.getAttributeValue(null, "max-device-sdk");
if (lname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
@@ -889,15 +886,18 @@ public class SystemConfig {
Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT;
boolean allowedMinSdk =
minDeviceSdk == null || UnboundedSdkLevel.isAtLeast(
minDeviceSdk);
boolean allowedMaxSdk =
maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT;
maxDeviceSdk == null || UnboundedSdkLevel.isAtMost(
maxDeviceSdk);
final boolean exists = new File(lfile).exists();
if (allowedMinSdk && allowedMaxSdk && exists) {
int bcpSince = XmlUtils.readIntAttribute(parser,
"on-bootclasspath-since", 0);
int bcpBefore = XmlUtils.readIntAttribute(parser,
"on-bootclasspath-before", 0);
String bcpSince = parser.getAttributeValue(null,
"on-bootclasspath-since");
String bcpBefore = parser.getAttributeValue(null,
"on-bootclasspath-before");
SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
ldependency == null
? new String[0] : ldependency.split(":"),

View File

@@ -19,6 +19,7 @@ package com.android.server.pm.parsing.library;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -51,8 +52,11 @@ public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater {
private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry,
ParsedPackage parsedPackage) {
if (entry.onBootclasspathBefore != 0
&& parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) {
if (entry.onBootclasspathBefore != null
&& isTargetSdkAtMost(
parsedPackage.getTargetSdkVersion(),
entry.onBootclasspathBefore)
&& UnboundedSdkLevel.isAtLeast(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);
@@ -64,4 +68,19 @@ public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater {
removeLibrary(parsedPackage, entry.name);
}
}
private static boolean isTargetSdkAtMost(int targetSdk, String onBcpBefore) {
if (isCodename(onBcpBefore)) {
return targetSdk < 10000;
}
return targetSdk < Integer.parseInt(onBcpBefore);
}
private static boolean isCodename(String version) {
if (version.length() == 0) {
throw new IllegalArgumentException();
}
// assume Android codenames start with upper case letters.
return Character.isUpperCase((version.charAt(0)));
}
}

View File

@@ -41,6 +41,8 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest {
private static final String SDK_INT_PLUS_ONE = "" + (Build.VERSION.SDK_INT + 1);
private static final String SDK_INT_PLUS_TWO = "" + (Build.VERSION.SDK_INT + 2);
private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries =
new ArrayMap<>(8);
@@ -51,14 +53,19 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
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);
insertLibrary("foo", null, null);
insertLibrary("fooBcpSince30", "30", null);
insertLibrary("fooBcpBefore30", null, "30");
// simulate libraries being added to the BCP in a future release
insertLibrary("fooSinceFuture", SDK_INT_PLUS_ONE, null);
insertLibrary("fooSinceFutureCodename", "Z", null);
// simulate libraries being removed from the BCP in a future release
insertLibrary("fooBcpBeforeFuture", null, SDK_INT_PLUS_ONE);
insertLibrary("fooBcpBeforeFutureCodename", null, "Z");
}
private void insertLibrary(String libraryName, int onBootclasspathSince,
int onBootclasspathBefore) {
private void insertLibrary(String libraryName, String onBootclasspathSince,
String onBootclasspathBefore) {
mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry(
libraryName,
"foo.jar",
@@ -112,7 +119,7 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
}
@Test
public void testBcpSince11kNotAppliedWithoutLibrary() {
public void testBcpSinceFutureNotAppliedWithoutLibrary() {
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
.hideAsParsed());
@@ -128,15 +135,17 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
}
@Test
public void testBcpSince11kNotAppliedWithLibrary() {
public void testBcpSinceFutureNotAppliedWithLibrary() {
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
.addUsesLibrary("fooFromFuture")
.addUsesLibrary("fooSinceFuture")
.addUsesLibrary("fooSinceFutureCodename")
.hideAsParsed());
AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
.addUsesLibrary("fooFromFuture")
.addUsesLibrary("fooSinceFuture")
.addUsesLibrary("fooSinceFutureCodename")
.hideAsParsed())
.hideAsFinal();
@@ -183,7 +192,7 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
*/
@Test
public void testBcpRemovedThenAddedPast() {
insertLibrary("fooBcpRemovedThenAdded", 30, 28);
insertLibrary("fooBcpRemovedThenAdded", "30", "28");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.N)
@@ -207,7 +216,8 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
*/
@Test
public void testBcpRemovedThenAddedMiddle_targetQ() {
insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
@@ -217,6 +227,7 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary("fooBcpRemovedThenAdded")
.addUsesLibrary("fooBcpBefore30")
.addUsesLibrary("fooBcpRemovedThenAddedCodename")
.hideAsParsed())
.hideAsFinal();
@@ -232,7 +243,8 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
*/
@Test
public void testBcpRemovedThenAddedMiddle_targetR() {
insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
@@ -256,7 +268,8 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
*/
@Test
public void testBcpRemovedThenAddedMiddle_targetR_usingLib() {
insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
@@ -274,6 +287,82 @@ public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTes
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. Both things happening in future SDKs.
*/
@Test
public void testBcpRemovedThenAddedFuture() {
insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE);
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 still in the BCP
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. Both things happening in future SDKs.
*/
@Test
public void testBcpRemovedThenAddedFuture_usingLib() {
insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE);
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
.addUsesLibrary("fooBcpRemovedThenAdded")
.hideAsParsed());
AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
.hideAsParsed())
.hideAsFinal();
// in this example, we are at the point where the library was removed from the BCP
checkBackwardsCompatibility(before, after);
}
@Test
public void testBcpBeforeFuture() {
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.R)
.addUsesLibrary("fooBcpBeforeFuture")
.addUsesLibrary("fooBcpBeforeFutureCodename")
.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 was removed from the BCP
checkBackwardsCompatibility(before, after);
}
@Test
public void testBcpBeforeFuture_futureTargetSdk() {
ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
.addUsesLibrary("fooBcpBeforeFuture")
.addUsesLibrary("fooBcpBeforeFutureCodename")
.hideAsParsed());
AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
.hideAsParsed())
.hideAsFinal();
// in this example, we are at the point where the library was removed from the BCP
checkBackwardsCompatibility(before, after);
}
private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
checkBackwardsCompatibility(before, after,
() -> new ApexSharedLibraryUpdater(mSharedLibraries));

View File

@@ -429,18 +429,40 @@ public class SystemConfigTest {
public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException {
String contents =
"<permissions>\n"
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
+ " on-bootclasspath-before=\"10\"\n"
+ " on-bootclasspath-since=\"20\"\n"
+ " />\n\n"
+ " </permissions>";
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
+ " on-bootclasspath-before=\"10\"\n"
+ " on-bootclasspath-since=\"20\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
assertThat(entry.onBootclasspathBefore).isEqualTo(10);
assertThat(entry.onBootclasspathSince).isEqualTo(20);
assertThat(entry.onBootclasspathBefore).isEqualTo("10");
assertThat(entry.onBootclasspathSince).isEqualTo("20");
}
/**
* Tests that readPermissions works correctly for a library with on-bootclasspath-before
* and on-bootclasspath-since that uses codenames.
*/
@Test
public void readPermissions_allowLibs_parsesSimpleLibraryWithCodenames() throws IOException {
String contents =
"<permissions>\n"
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
+ " on-bootclasspath-before=\"Q\"\n"
+ " on-bootclasspath-since=\"W\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
assertThat(entry.onBootclasspathSince).isEqualTo("W");
}
/**
@@ -461,8 +483,8 @@ public class SystemConfigTest {
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
assertThat(entry.onBootclasspathBefore).isEqualTo(10);
assertThat(entry.onBootclasspathSince).isEqualTo(20);
assertThat(entry.onBootclasspathBefore).isEqualTo("10");
assertThat(entry.onBootclasspathSince).isEqualTo("20");
}
/**
@@ -543,12 +565,20 @@ public class SystemConfigTest {
*/
@Test
public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException {
// depending on whether this test is running before or after finalization, we need to
// pass a different parameter
String parameter;
if ("REL".equals(Build.VERSION.CODENAME)) {
parameter = "" + Build.VERSION.SDK_INT;
} else {
parameter = "ZZZ";
}
String contents =
"<permissions>\n"
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
+ " max-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n"
+ " max-device-sdk=\"" + parameter + "\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);