diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 9b583be547c3d..00b01051adae9 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -339,6 +339,8 @@ public class SystemConfig { // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. private final ArrayMap mAllowedVendorApexes = new ArrayMap<>(); + // A set of package names that are allowed to use manifest tag. + private final Set mInstallConstraintsAllowlist = new ArraySet<>(); private String mModulesInstallerPackageName; @@ -535,6 +537,10 @@ public class SystemConfig { return mAllowedVendorApexes; } + public Set getInstallConstraintsAllowlist() { + return mInstallConstraintsAllowlist; + } + public String getModulesInstallerPackageName() { return mModulesInstallerPackageName; } @@ -1455,6 +1461,20 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "install-constraints-allowed": { + if (allowAppConfigs) { + String packageName = parser.getAttributeValue(null, "package"); + if (packageName == null) { + Slog.w(TAG, "<" + name + "> without package in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mInstallConstraintsAllowlist.add(packageName); + } + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; default: { Slog.w(TAG, "Tag " + name + " is unknown in " + permFile + " at " + parser.getPositionDescription()); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 7562b9aa0eada..a62f6ad4681ac 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -3602,4 +3602,19 @@ false, the application cannot be profiled at all. Defaults to true. --> + + + + + + + + diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java new file mode 100644 index 0000000000000..14976848fe612 --- /dev/null +++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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.pkg.component; + + +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.os.Build; +import android.util.ArraySet; + +import com.android.internal.R; +import com.android.server.SystemConfig; +import com.android.server.pm.pkg.parsing.ParsingPackage; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.Set; + +/** + * Utility methods for handling the tag {@code } + * + * @hide + */ +public class InstallConstraintsTagParser { + + private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix"; + + /** + * @hide + */ + public static ParseResult parseInstallConstraints( + ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + Set allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist(); + if (!allowlist.contains(pkg.getPackageName())) { + return input.skip("install-constraints cannot be used by this package"); + } + + ParseResult> prefixes = parseFingerprintPrefixes(input, res, parser); + if (prefixes.isSuccess()) { + if (validateFingerprintPrefixes(prefixes.getResult())) { + return input.success(pkg); + } else { + return input.skip( + "Install of this package is restricted on this device; device fingerprint" + + " does not start with one of the allowed prefixes"); + } + } + return input.skip(prefixes.getErrorMessage()); + } + + private static ParseResult> parseFingerprintPrefixes( + ParseInput input, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + Set prefixes = new ArraySet<>(); + int type; + while (true) { + // move to the tag that contains the next prefix + type = parser.next(); + if (type == XmlPullParser.END_TAG) { + if (prefixes.size() == 0) { + return input.error("install-constraints must contain at least one constraint"); + } + return input.success(prefixes); + } else if (type == XmlPullParser.START_TAG) { + if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) { + ParseResult parsedPrefix = + readFingerprintPrefixValue(input, res, parser); + if (parsedPrefix.isSuccess()) { + prefixes.add(parsedPrefix.getResult()); + } else { + return input.error(parsedPrefix.getErrorMessage()); + } + } else { + return input.error("Unexpected tag: " + parser.getName()); + } + + // consume the end tag of this attribute + type = parser.next(); + if (type != XmlPullParser.END_TAG) { + return input.error("Expected end tag; instead got " + type); + } + } + } + } + + private static ParseResult readFingerprintPrefixValue(ParseInput input, Resources res, + XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix); + try { + String value = sa.getString( + R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value); + if (value == null) { + return input.error("Failed to specify prefix value"); + } + return input.success(value); + } finally { + sa.recycle(); + } + } + + private static boolean validateFingerprintPrefixes(Set prefixes) { + String fingerprint = Build.FINGERPRINT; + for (String prefix : prefixes) { + if (fingerprint.startsWith(prefix)) { + return true; + } + } + return false; + } +} diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index 9bfb40fe11f7d..7ce7f7ebf6cc2 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -95,6 +95,7 @@ import com.android.server.pm.SharedUidMigration; import com.android.server.pm.permission.CompatibilityPermissionInfo; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ComponentParseUtils; +import com.android.server.pm.pkg.component.InstallConstraintsTagParser; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedActivityUtils; import com.android.server.pm.pkg.component.ParsedApexSystemService; @@ -169,9 +170,11 @@ public class ParsingPackageUtils { public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; public static final String TAG_APPLICATION = "application"; + public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; public static final String TAG_EAT_COMMENT = "eat-comment"; public static final String TAG_FEATURE_GROUP = "feature-group"; + public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints"; public static final String TAG_INSTRUMENTATION = "instrumentation"; public static final String TAG_KEY_SETS = "key-sets"; public static final String TAG_MANIFEST = "manifest"; @@ -179,15 +182,16 @@ public class ParsingPackageUtils { public static final String TAG_OVERLAY = "overlay"; public static final String TAG_PACKAGE = "package"; public static final String TAG_PACKAGE_VERIFIER = "package-verifier"; - public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_PERMISSION = "permission"; public static final String TAG_PERMISSION_GROUP = "permission-group"; public static final String TAG_PERMISSION_TREE = "permission-tree"; + public static final String TAG_PROFILEABLE = "profileable"; public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; public static final String TAG_QUERIES = "queries"; + public static final String TAG_RECEIVER = "receiver"; public static final String TAG_RESTRICT_UPDATE = "restrict-update"; - public static final String TAG_SUPPORT_SCREENS = "supports-screens"; public static final String TAG_SUPPORTS_INPUT = "supports-input"; + public static final String TAG_SUPPORT_SCREENS = "supports-screens"; public static final String TAG_USES_CONFIGURATION = "uses-configuration"; public static final String TAG_USES_FEATURE = "uses-feature"; public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; @@ -196,8 +200,6 @@ public class ParsingPackageUtils { public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; public static final String TAG_USES_SDK = "uses-sdk"; public static final String TAG_USES_SPLIT = "uses-split"; - public static final String TAG_PROFILEABLE = "profileable"; - public static final String TAG_RECEIVER = "receiver"; public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes"; @@ -1040,6 +1042,8 @@ public class ParsingPackageUtils { return input.success(pkg); case TAG_RESTRICT_UPDATE: return parseRestrictUpdateHash(flags, input, pkg, res, parser); + case TAG_INSTALL_CONSTRAINTS: + return parseInstallConstraints(input, pkg, res, parser); case TAG_QUERIES: return parseQueries(input, pkg, res, parser); default: @@ -1729,6 +1733,12 @@ public class ParsingPackageUtils { return input.success(pkg); } + private static ParseResult parseInstallConstraints( + ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws IOException, XmlPullParserException { + return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser); + } + private static ParseResult parseQueries(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { final int depth = parser.getDepth(); diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index e9171c0c35144..92c7871e611d1 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -336,6 +336,59 @@ public class SystemConfigTest { assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty(); } + /** + * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. + */ + @Test + public void readPermissions_installConstraints_successful() throws IOException { + final String contents = + "\n" + + " \n" + + ""; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "install-constraints-allowlist.xml", contents); + + readPermissions(folder, /* Grant all permission flags */ ~0); + + assertThat(mSysConfig.getInstallConstraintsAllowlist()) + .containsExactly("com.android.apex1"); + } + + /** + * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. + */ + @Test + public void readPermissions_installConstraints_noPackage() throws IOException { + final String contents = + "\n" + + " \n" + + ""; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "install-constraints-allowlist.xml", contents); + + readPermissions(folder, /* Grant all permission flags */ ~0); + + assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); + } + + /** + * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed} + * without {@link SystemConfig#ALLOW_VENDOR_APEX}. + */ + @Test + public void readPermissions_installConstraints_noAppConfigs() throws IOException { + final String contents = + "\n" + + " \n" + + ""; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "install-constraints-allowlist.xml", contents); + + readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); + + assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); + } + @Test public void readApexPrivAppPermissions_addAllPermissions() throws Exception { diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index d432341a8cded..948b11b6795ef 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -477,6 +477,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["compatible-screens"]["screen"]; manifest_action["supports-gl-texture"]; manifest_action["restrict-update"]; + manifest_action["install-constraints"]["fingerprint-prefix"]; manifest_action["package-verifier"]; manifest_action["meta-data"] = meta_data_action; manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);