Add apex-system-service tag to AndroidManifest.

This change introduces a new <apex-system-service> tag to the manifest.

A manifest may have multiple <apex-system-service> 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
This commit is contained in:
Gavin Corkery
2021-10-26 12:24:43 +01:00
committed by satayev
parent f7d3ea8718
commit ad5d4baecb
12 changed files with 444 additions and 12 deletions

View File

@@ -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);

View File

@@ -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<ParsedActivity> activities = emptyList();
@NonNull
protected List<ParsedApexSystemService> apexSystemServices = emptyList();
@NonNull
protected List<ParsedActivity> 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<ParsedApexSystemService> getApexSystemServices() {
return apexSystemServices;
}
@NonNull
@Override
public List<ParsedActivity> getReceivers() {

View File

@@ -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<String> getAdoptPermissions();
/**
* @see R.styleable#AndroidManifestApexSystemService
*/
@NonNull
List<ParsedApexSystemService> getApexSystemServices();
@NonNull
List<ParsedAttribution> getAttributions();

View File

@@ -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<ParsedApexSystemService> 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;

View File

@@ -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();
}

View File

@@ -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<String> 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<String> 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<String> 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<String> 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<ParsedApexSystemServiceImpl> CREATOR
= new android.os.Parcelable.Creator<ParsedApexSystemServiceImpl>() {
@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
}

View File

@@ -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<ParsedApexSystemService> 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("<apex-system-service> 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();
}
}
}

View File

@@ -2808,6 +2808,22 @@
<attr name="attributionTags" />
</declare-styleable>
<!-- @hide The <code>apex-system-service</code> tag declares an apex system service
that is contained within an application.
The apex system service tag appears as a child tag of the
{@link #AndroidManifestApplication application} tag. -->
<declare-styleable name="AndroidManifestApexSystemService"
parent="AndroidManifestApplication">
<!-- The fully qualified class name of the system service. -->
<attr name="name" />
<!-- The filepath to the .jar that contains the system service. If this is not provided, it
is assumed that the system service exists in SYSTEMSERVERCLASSPATH. -->
<attr name="path" />
<attr name="minSdkVersion" />
<attr name="maxSdkVersion" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
{@link android.content.BroadcastReceiver} class that is available
as part of the package's application components, allowing the
@@ -3051,6 +3067,7 @@
<declare-styleable name="AndroidManifestMetaData"
parent="AndroidManifestApplication
AndroidManifestActivity
AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService
@@ -3085,6 +3102,7 @@
<declare-styleable name="AndroidManifestProperty"
parent="AndroidManifestApplication
AndroidManifestActivity
AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService">

View File

@@ -24,17 +24,7 @@ import android.content.pm.FeatureInfo
import android.content.pm.PackageManager
import android.content.pm.SigningDetails
import android.content.pm.parsing.ParsingPackage
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedAttributionImpl
import android.content.pm.parsing.component.ParsedComponentImpl
import android.content.pm.parsing.component.ParsedInstrumentationImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
import android.content.pm.parsing.component.ParsedPermissionGroupImpl
import android.content.pm.parsing.component.ParsedPermissionImpl
import android.content.pm.parsing.component.ParsedProcessImpl
import android.content.pm.parsing.component.ParsedProviderImpl
import android.content.pm.parsing.component.ParsedServiceImpl
import android.content.pm.parsing.component.ParsedUsesPermissionImpl
import android.content.pm.parsing.component.*
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
@@ -359,6 +349,13 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
transformGet = { it.singleOrNull()?.name.orEmpty() },
transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
),
getSetByValue(
AndroidPackage::getApexSystemServices,
PackageImpl::addApexSystemService,
"TestApexSystemServiceName",
transformGet = { it.singleOrNull()?.name.orEmpty() },
transformSet = { ParsedApexSystemServiceImpl().apply { name = it } }
),
getSetByValue(
AndroidPackage::getReceivers,
PackageImpl::addReceiver,

View File

@@ -46,6 +46,7 @@ import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
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.ParsedComponent;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedInstrumentationImpl;
@@ -525,6 +526,23 @@ public class PackageParserTest {
}
}
@Test
public void testParseApexSystemService() throws Exception {
final File testFile = extractFile(TEST_APP4_APK);
try {
final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
final List<ParsedApexSystemService> systemServices = pkg.getApexSystemServices();
for (ParsedApexSystemService systemService: systemServices) {
assertEquals(PACKAGE_NAME + ".SystemService", systemService.getName());
assertEquals("service-test.jar", systemService.getJarPath());
assertEquals("30", systemService.getMinSdkVersion());
assertEquals("31", systemService.getMaxSdkVersion());
}
} finally {
testFile.delete();
}
}
@Test
public void testParseModernPackageHasNoCompatPermissions() throws Exception {
final File testFile = extractFile(TEST_APP1_APK);

View File

@@ -57,6 +57,11 @@
<property android:name="android.cts.PROPERTY_SERVICE" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_COMPONENT" android:resource="@integer/integer_property" />
</service>
<apex-system-service
android:name="com.android.servicestests.apps.packageparserapp.SystemService"
android:path="service-test.jar"
android:minSdkVersion = "30"
android:maxSdkVersion = "31" />
</application>
<instrumentation

View File

@@ -544,6 +544,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action["activity-alias"] = component_action;
application_action["service"] = component_action;
application_action["receiver"] = component_action;
application_action["apex-system-service"] = component_action;
// Provider actions.
application_action["provider"] = component_action;