Adds queries tag to manifest
This change adds support for a new <queries> tag as a child of the
<manifest> tag, allowing a package to declare the intents it intends to
use to query for other apps on device.
Bug: 136675067
Test: build and boot
Change-Id: Ic8b40cabddee4b6404c220901efeaae680661c0c
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e690a7f..a4933ef 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -66,6 +66,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
@@ -2445,6 +2446,8 @@
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
+ } else if (tagName.equals("queries")) {
+ parseQueries(pkg, res, parser, flags, outError);
} else {
Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
@@ -3538,6 +3541,9 @@
owner.mRequiredAccountType = requiredAccountType;
}
+ owner.mForceQueryable =
+ sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false);
+
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
false)) {
@@ -3953,7 +3959,6 @@
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
}
XmlUtils.skipCurrentTag(parser);
-
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
@@ -4000,6 +4005,67 @@
return true;
}
+ private boolean parseQueries(Package owner, Resources res, XmlResourceParser parser, int flags,
+ String[] outError)
+ throws IOException, XmlPullParserException {
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (parser.getName().equals("intent")) {
+ QueriesIntentInfo intentInfo = new QueriesIntentInfo();
+ if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
+ intentInfo, outError)) {
+ return false;
+ }
+ Intent intent = new Intent();
+ if (intentInfo.countActions() != 1) {
+ outError[0] = "intent tags must contain exactly one action.";
+ return false;
+ }
+ intent.setAction(intentInfo.getAction(0));
+ for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+ intent.addCategory(intentInfo.getCategory(i));
+ }
+ Uri data = null;
+ String dataType = null;
+ if (intentInfo.countDataTypes() > 1) {
+ outError[0] = "intent tag may have at most one data type.";
+ return false;
+ }
+ if (intentInfo.countDataSchemes() > 1) {
+ outError[0] = "intent tag may have at most one data scheme.";
+ return false;
+ }
+ if (intentInfo.countDataTypes() == 1) {
+ data = Uri.fromParts(intentInfo.getDataType(0), "", null);
+ }
+ if (intentInfo.countDataSchemes() == 1) {
+ dataType = intentInfo.getDataScheme(0);
+ }
+ intent.setDataAndType(data, dataType);
+ owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent);
+ } else if (parser.getName().equals("package")) {
+ final TypedArray sa = res.obtainAttributes(parser,
+ com.android.internal.R.styleable.AndroidManifestQueriesPackage);
+ final String packageName =
+ sa.getString(R.styleable.AndroidManifestQueriesPackage_name);
+ if (TextUtils.isEmpty(packageName)) {
+ outError[0] = "Package name is missing from package tag.";
+ return false;
+ }
+ owner.mQueriesPackages =
+ ArrayUtils.add(owner.mQueriesPackages, packageName.intern());
+ }
+ }
+ return true;
+ }
+
/**
* Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
*/
@@ -6514,6 +6580,9 @@
// The major version code declared for this package.
public int mVersionCodeMajor;
+ // Whether the package declares that it should be queryable by all normal apps on device.
+ public boolean mForceQueryable;
+
// Return long containing mVersionCode and mVersionCodeMajor.
public long getLongVersionCode() {
return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
@@ -6619,6 +6688,9 @@
/** Whether or not the package is a stub and must be replaced by the full version. */
public boolean isStub;
+ public ArrayList<String> mQueriesPackages;
+ public ArrayList<Intent> mQueriesIntents;
+
@UnsupportedAppUsage
public Package(String packageName) {
this.packageName = packageName;
@@ -7122,6 +7194,9 @@
use32bitAbi = (dest.readInt() == 1);
restrictUpdateHash = dest.createByteArray();
visibleToInstantApps = dest.readInt() == 1;
+ mForceQueryable = dest.readBoolean();
+ mQueriesIntents = dest.createTypedArrayList(Intent.CREATOR);
+ mQueriesPackages = dest.createStringArrayList();
}
private static void internStringArrayList(List<String> list) {
@@ -7247,6 +7322,9 @@
dest.writeInt(use32bitAbi ? 1 : 0);
dest.writeByteArray(restrictUpdateHash);
dest.writeInt(visibleToInstantApps ? 1 : 0);
+ dest.writeBoolean(mForceQueryable);
+ dest.writeTypedList(mQueriesIntents);
+ dest.writeList(mQueriesPackages);
}
@@ -8257,6 +8335,8 @@
}
}
+ public static final class QueriesIntentInfo extends IntentInfo {}
+
public final static class ActivityIntentInfo extends IntentInfo {
@UnsupportedAppUsage
public Activity activity;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 40acebc..7ddf1b13 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4598,6 +4598,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.DeviceDefault.Light.DarkActionBar"
android:defaultToDeviceProtectedStorage="true"
+ android:forceQueryable="true"
android:directBootAware="true">
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.DeviceDefault.Resolver"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index be6cdcf..3ea8a77 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1743,6 +1743,13 @@
- {@code true} for apps with targetSdkVersion < 29.
-->
<attr name="requestLegacyExternalStorage" format="boolean" />
+
+ <!-- If {@code true} this app declares that it should be visible to all other apps on
+ device, regardless of what they declare via the {@code queries} tags in their
+ manifest.
+
+ The default value is {@code false}. -->
+ <attr name="forceQueryable" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
@@ -1977,6 +1984,12 @@
<attr name="name" />
</declare-styleable>
+ <declare-styleable name="AndroidManifestQueries" parent="AndroidManifest" />
+ <declare-styleable name="AndroidManifestQueriesPackage" parent="AndroidManifestQueries">
+ <attr name="name" />
+ </declare-styleable>
+ <declare-styleable name="AndroidManifestQueriesIntent" parent="AndroidManifestQueries" />
+
<!-- The <code>static-library</code> tag declares that this apk is providing itself
as a static shared library for other applications to use. Any app can declare such
@@ -2477,6 +2490,7 @@
<!-- High dynamic range color mode. -->
<enum name="hdr" value="2" />
</attr>
+ <attr name="forceQueryable" format="boolean" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3b12753..dce4bf3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1700,6 +1700,12 @@
<!-- Add packages here -->
</string-array>
+ <string-array name="config_forceQueryablePackages" translatable="false">
+ <item>com.android.settings</item>
+ <!-- Add packages here -->
+ </string-array>
+
+
<!-- Component name of the default wallpaper. This will be ImageWallpaper if not
specified -->
<string name="default_wallpaper_component" translatable="false">@null</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d91bbd0..123deeb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -786,6 +786,7 @@
<java-symbol type="string" name="widget_default_class_name" />
<java-symbol type="string" name="emergency_calls_only" />
<java-symbol type="array" name="config_ephemeralResolverPackage" />
+ <java-symbol type="array" name="config_forceQueryablePackages" />
<java-symbol type="string" name="eventTypeAnniversary" />
<java-symbol type="string" name="eventTypeBirthday" />
<java-symbol type="string" name="eventTypeCustom" />
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 13a8eb1..e33d8ca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
@@ -557,6 +558,9 @@
pkg.mRequiredForAllUsers = true;
pkg.visibleToInstantApps = true;
pkg.use32bitAbi = true;
+ pkg.mForceQueryable = true;
+ pkg.mQueriesPackages = new ArrayList<>(Arrays.asList("foo27"));
+ pkg.mQueriesIntents = new ArrayList<>(Arrays.asList(new Intent("foo28")));
}
private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 11150dd..b725920 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -386,6 +386,9 @@
manifest_action["package-verifier"];
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
+ manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage);
+ manifest_action["queries"]["intent"] = intent_filter_action;
+ // TODO: more complicated component name tag
manifest_action["key-sets"]["key-set"]["public-key"];
manifest_action["key-sets"]["upgrade-key-set"];