From ad5d4baecbaee6e5c007d7d77beac0eac3339360 Mon Sep 17 00:00:00 2001 From: Gavin Corkery Date: Tue, 26 Oct 2021 12:24:43 +0100 Subject: [PATCH] Add apex-system-service tag to AndroidManifest. This change introduces a new tag to the manifest. A manifest may have multiple tags, with each tag denoting one system service that lives in an apex. If the service is not in SYSTEMSERVERCLASSPATH, the android:path attribute may be used to indicate where the jar is located on the filesystem. The minSdkVersion and maxSdkVersion attributes may be used to indicate which SDKs the service should start on. Test: atest PackageParserTest Bug: 192880996 Change-Id: I28e595b397ed8db6c412f490f12c73537972efab --- .../content/pm/parsing/ParsingPackage.java | 3 + .../pm/parsing/ParsingPackageImpl.java | 23 +- .../pm/parsing/ParsingPackageRead.java | 7 + .../pm/parsing/ParsingPackageUtils.java | 14 + .../component/ParsedApexSystemService.java | 38 +++ .../ParsedApexSystemServiceImpl.java | 241 ++++++++++++++++++ .../ParsedApexSystemServiceUtils.java | 69 +++++ core/res/res/values/attrs_manifest.xml | 18 ++ .../parsing/parcelling/AndroidPackageTest.kt | 19 +- .../android/server/pm/PackageParserTest.java | 18 ++ .../PackageParserApp/AndroidManifestApp4.xml | 5 + tools/aapt2/link/ManifestFixer.cpp | 1 + 12 files changed, 444 insertions(+), 12 deletions(-) create mode 100644 core/java/android/content/pm/parsing/component/ParsedApexSystemService.java create mode 100644 core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java create mode 100644 core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 63332e79c88b9..2fa5df72656bd 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -27,6 +27,7 @@ import android.content.pm.FeatureInfo; import android.content.pm.PackageManager.Property; import android.content.pm.SigningDetails; import android.content.pm.parsing.component.ParsedActivity; +import android.content.pm.parsing.component.ParsedApexSystemService; import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; @@ -56,6 +57,8 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage addAdoptPermission(String adoptPermission); + ParsingPackage addApexSystemService(ParsedApexSystemService parsedApexSystemService); + ParsingPackage addConfigPreference(ConfigurationInfo configPreference); ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 19a8ce92373da..f03ab6ab94f29 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -35,6 +35,8 @@ import android.content.pm.PackageManager.Property; import android.content.pm.SigningDetails; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedActivityImpl; +import android.content.pm.parsing.component.ParsedApexSystemService; +import android.content.pm.parsing.component.ParsedApexSystemServiceImpl; import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedAttributionImpl; import android.content.pm.parsing.component.ParsedComponent; @@ -267,6 +269,9 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, @NonNull protected List activities = emptyList(); + @NonNull + protected List apexSystemServices = emptyList(); + @NonNull protected List receivers = emptyList(); @@ -763,6 +768,14 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, return this; } + @Override + public final ParsingPackageImpl addApexSystemService( + ParsedApexSystemService parsedApexSystemService) { + this.apexSystemServices = CollectionUtils.add( + this.apexSystemServices, parsedApexSystemService); + return this; + } + @Override public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) { this.receivers = CollectionUtils.add(this.receivers, parsedReceiver); @@ -832,7 +845,6 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, return this; } - @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) { this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries, libraryName); @@ -1198,6 +1210,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping); sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags); dest.writeTypedList(this.activities); + dest.writeTypedList(this.apexSystemServices); dest.writeTypedList(this.receivers); dest.writeTypedList(this.services); dest.writeTypedList(this.providers); @@ -1339,6 +1352,8 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, this.protectedBroadcasts = sForInternedStringList.unparcel(in); this.activities = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR); + this.apexSystemServices = ParsingUtils.createTypedInterfaceList(in, + ParsedApexSystemServiceImpl.CREATOR); this.receivers = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR); this.services = ParsingUtils.createTypedInterfaceList(in, ParsedServiceImpl.CREATOR); this.providers = ParsingUtils.createTypedInterfaceList(in, ParsedProviderImpl.CREATOR); @@ -1704,6 +1719,12 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, return activities; } + @NonNull + @Override + public List getApexSystemServices() { + return apexSystemServices; + } + @NonNull @Override public List getReceivers() { diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index 49b3b08ec5dbe..c8113efcc7c1d 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.Property; import android.content.pm.PackageParser; import android.content.pm.SigningDetails; +import android.content.pm.parsing.component.ParsedApexSystemService; import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedPermissionGroup; @@ -55,6 +56,12 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt @NonNull List getAdoptPermissions(); + /** + * @see R.styleable#AndroidManifestApexSystemService + */ + @NonNull + List getApexSystemServices(); + @NonNull List getAttributions(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 3e537c8741377..fb24cb2ef3bd5 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -53,6 +53,8 @@ import android.content.pm.parsing.component.ComponentMutateUtils; import android.content.pm.parsing.component.ComponentParseUtils; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedActivityUtils; +import android.content.pm.parsing.component.ParsedApexSystemService; +import android.content.pm.parsing.component.ParsedApexSystemServiceUtils; import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedAttributionUtils; import android.content.pm.parsing.component.ParsedComponent; @@ -2205,6 +2207,18 @@ public class ParsingPackageUtils { result = activityResult; break; + case "apex-system-service": + ParseResult systemServiceResult = + ParsedApexSystemServiceUtils.parseApexSystemService(res, + parser, input); + if (systemServiceResult.isSuccess()) { + ParsedApexSystemService systemService = + systemServiceResult.getResult(); + pkg.addApexSystemService(systemService); + } + + result = systemServiceResult; + break; default: result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags); break; diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java new file mode 100644 index 0000000000000..fe821e04958f0 --- /dev/null +++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java @@ -0,0 +1,38 @@ +/* + * 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 android.content.pm.parsing.component; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; + +/** @hide */ +public interface ParsedApexSystemService extends Parcelable { + + @NonNull + String getName(); + + @Nullable + String getJarPath(); + + @Nullable + String getMinSdkVersion(); + + @Nullable + String getMaxSdkVersion(); + +} diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java new file mode 100644 index 0000000000000..54196fddb1fb2 --- /dev/null +++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java @@ -0,0 +1,241 @@ +/* + * 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 android.content.pm.parsing.component; + + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + +/** @hide **/ +@DataClass(genGetters = true, genAidl = false, genSetters = true, genParcelable = true) +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public class ParsedApexSystemServiceImpl implements ParsedApexSystemService { + + @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class) + @NonNull + private String name; + + @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class) + @Nullable + private String jarPath; + + @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class) + @Nullable + private String minSdkVersion; + + @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class) + @Nullable + private String maxSdkVersion; + + public ParsedApexSystemServiceImpl() { + } + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public ParsedApexSystemServiceImpl( + @NonNull String name, + @Nullable String jarPath, + @Nullable String minSdkVersion, + @Nullable String maxSdkVersion) { + this.name = name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, name); + this.jarPath = jarPath; + this.minSdkVersion = minSdkVersion; + this.maxSdkVersion = maxSdkVersion; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull String getName() { + return name; + } + + @DataClass.Generated.Member + public @Nullable String getJarPath() { + return jarPath; + } + + @DataClass.Generated.Member + public @Nullable String getMinSdkVersion() { + return minSdkVersion; + } + + @DataClass.Generated.Member + public @Nullable String getMaxSdkVersion() { + return maxSdkVersion; + } + + @DataClass.Generated.Member + public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) { + name = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, name); + return this; + } + + @DataClass.Generated.Member + public @NonNull ParsedApexSystemServiceImpl setJarPath(@NonNull String value) { + jarPath = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull ParsedApexSystemServiceImpl setMinSdkVersion(@NonNull String value) { + minSdkVersion = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull ParsedApexSystemServiceImpl setMaxSdkVersion(@NonNull String value) { + maxSdkVersion = value; + return this; + } + + @DataClass.Generated.Member + static Parcelling sParcellingForName = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForInternedString.class); + static { + if (sParcellingForName == null) { + sParcellingForName = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForInternedString()); + } + } + + @DataClass.Generated.Member + static Parcelling sParcellingForJarPath = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForInternedString.class); + static { + if (sParcellingForJarPath == null) { + sParcellingForJarPath = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForInternedString()); + } + } + + @DataClass.Generated.Member + static Parcelling sParcellingForMinSdkVersion = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForInternedString.class); + static { + if (sParcellingForMinSdkVersion == null) { + sParcellingForMinSdkVersion = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForInternedString()); + } + } + + @DataClass.Generated.Member + static Parcelling sParcellingForMaxSdkVersion = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForInternedString.class); + static { + if (sParcellingForMaxSdkVersion == null) { + sParcellingForMaxSdkVersion = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForInternedString()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (jarPath != null) flg |= 0x2; + if (minSdkVersion != null) flg |= 0x4; + if (maxSdkVersion != null) flg |= 0x8; + dest.writeByte(flg); + sParcellingForName.parcel(name, dest, flags); + sParcellingForJarPath.parcel(jarPath, dest, flags); + sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags); + sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected ParsedApexSystemServiceImpl(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String _name = sParcellingForName.unparcel(in); + String _jarPath = sParcellingForJarPath.unparcel(in); + String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in); + String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in); + + this.name = _name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, name); + this.jarPath = _jarPath; + this.minSdkVersion = _minSdkVersion; + this.maxSdkVersion = _maxSdkVersion; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull android.os.Parcelable.Creator CREATOR + = new android.os.Parcelable.Creator() { + @Override + public ParsedApexSystemServiceImpl[] newArray(int size) { + return new ParsedApexSystemServiceImpl[size]; + } + + @Override + public ParsedApexSystemServiceImpl createFromParcel(@NonNull android.os.Parcel in) { + return new ParsedApexSystemServiceImpl(in); + } + }; + + @DataClass.Generated( + time = 1638903241144L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java new file mode 100644 index 0000000000000..26abf48ee3915 --- /dev/null +++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java @@ -0,0 +1,69 @@ +/* + * 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 android.content.pm.parsing.component; + +import android.R; +import android.annotation.NonNull; +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseResult; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.text.TextUtils; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** @hide */ +public class ParsedApexSystemServiceUtils { + + @NonNull + public static ParseResult parseApexSystemService( + Resources res, XmlResourceParser parser, ParseInput input) + throws XmlPullParserException, IOException { + final ParsedApexSystemServiceImpl systemService = + new ParsedApexSystemServiceImpl(); + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestApexSystemService); + try { + String className = sa.getString( + R.styleable.AndroidManifestApexSystemService_name); + if (TextUtils.isEmpty(className)) { + return input.error(" does not have name attribute"); + } + + String jarPath = sa.getString( + R.styleable.AndroidManifestApexSystemService_path); + String minSdkVersion = sa.getString( + R.styleable.AndroidManifestApexSystemService_minSdkVersion); + String maxSdkVersion = sa.getString( + R.styleable.AndroidManifestApexSystemService_maxSdkVersion); + + systemService.setName(className) + .setMinSdkVersion(minSdkVersion) + .setMaxSdkVersion(maxSdkVersion); + if (!TextUtils.isEmpty(jarPath)) { + systemService.setJarPath(jarPath); + } + + return input.success(systemService); + } finally { + sa.recycle(); + } + } +} diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 91e4074b1463a..0a6ef7cf6f21f 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2808,6 +2808,22 @@ + + + + + + + + + +