Adds queries->provider tag

This change adds support for the <provider> tag inside of the <queries>
tag to support more succinct declaration that an app would like to see
the provider of a given authority.

Test: atest AppEnumerationTests AppsFilterTest
Bug: 136675067
Change-Id: Ie0f73213fae7a3a0619238e44063d4e5be157201
This commit is contained in:
Patrick Baumann
2020-01-28 10:55:25 -08:00
parent b82819fc4d
commit 9918123bac
8 changed files with 90 additions and 38 deletions

View File

@@ -286,6 +286,8 @@ public interface AndroidPackage extends Parcelable {
List<String> getQueriesPackages();
Set<String> getQueriesProviders();
String getRealPackage();
// TODO(b/135203078): Rename to getRequiredFeatures? Somewhat ambiguous whether "Req" is

View File

@@ -96,6 +96,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/** @hide */
public class ApkParseUtils {
@@ -1817,6 +1818,25 @@ public class ApkParseUtils {
);
}
parsingPackage.addQueriesPackage(packageName.intern());
} else if (parser.getName().equals("provider")) {
final TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestQueriesProvider);
try {
final String authorities =
sa.getString(R.styleable.AndroidManifestQueriesProvider_authorities);
if (TextUtils.isEmpty(authorities)) {
return parseInput.error(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Authority missing from provider tag."
);
}
StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
while (authoritiesTokenizer.hasMoreElements()) {
parsingPackage.addQueriesProvider(authoritiesTokenizer.nextToken());
}
} finally {
sa.recycle();
}
}
}
return parseInput.success(parsingPackage);

View File

@@ -215,6 +215,9 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
@Nullable
private ArrayList<String> queriesPackages;
@Nullable
private ArraySet<String> queriesProviders;
@Nullable
private ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;
@@ -956,6 +959,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
return this;
}
@Override
public ParsingPackage addQueriesProvider(String authority) {
this.queriesProviders = ArrayUtils.add(this.queriesProviders, authority);
return this;
}
@Override
public PackageImpl setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes) {
this.processes = processes;
@@ -2975,6 +2984,11 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
return queriesPackages;
}
@Override
public Set<String> getQueriesProviders() {
return queriesProviders;
}
private static void internStringArrayList(List<String> list) {
if (list != null) {
final int N = list.size();

View File

@@ -100,6 +100,8 @@ public interface ParsingPackage extends AndroidPackage {
ParsingPackage addQueriesPackage(String packageName);
ParsingPackage addQueriesProvider(String authority);
ParsingPackage setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes);
ParsingPackage asSplit(

View File

@@ -2056,6 +2056,9 @@
<attr name="name" />
</declare-styleable>
<declare-styleable name="AndroidManifestQueriesIntent" parent="AndroidManifestQueries" />
<declare-styleable name="AndroidManifestQueriesProvider" parent="AndroidManifestQueries" >
<attr name="authorities" />
</declare-styleable>
<!-- The <code>static-library</code> tag declares that this apk is providing itself

View File

@@ -33,7 +33,6 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
import android.content.pm.parsing.ComponentParseUtils.ParsedService;
import android.net.Uri;
import android.os.Binder;
import android.os.Process;
import android.os.Trace;
@@ -87,10 +86,10 @@ public class AppsFilter {
private final SparseSetArray<Integer> mQueriesViaPackage = new SparseSetArray<>();
/**
* A mapping from the set of App IDs that query others via intent to the list
* of packages that the intents resolve to.
* A mapping from the set of App IDs that query others via component match to the list
* of packages that the they resolve to.
*/
private final SparseSetArray<Integer> mQueriesViaIntent = new SparseSetArray<>();
private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
/**
* A set of App IDs that are always queryable by any package, regardless of their manifest
@@ -206,16 +205,19 @@ public class AppsFilter {
}
/** Returns true if the querying package may query for the potential target package */
private static boolean canQueryViaIntent(AndroidPackage querying,
private static boolean canQueryViaComponents(AndroidPackage querying,
AndroidPackage potentialTarget) {
if (querying.getQueriesIntents() == null) {
return false;
}
for (Intent intent : querying.getQueriesIntents()) {
if (matches(intent, potentialTarget)) {
return true;
if (querying.getQueriesIntents() != null) {
for (Intent intent : querying.getQueriesIntents()) {
if (matchesIntentFilters(intent, potentialTarget)) {
return true;
}
}
}
if (querying.getQueriesProviders() != null
&& matchesProviders(querying.getQueriesProviders(), potentialTarget)) {
return true;
}
return false;
}
@@ -238,24 +240,25 @@ public class AppsFilter {
return false;
}
private static boolean matches(Intent intent, AndroidPackage potentialTarget) {
private static boolean matchesProviders(
Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
ParsedProvider provider = potentialTarget.getProviders().get(p);
if (!provider.isExported()) {
continue;
}
final Uri data = intent.getData();
if (!"content".equalsIgnoreCase(intent.getScheme()) || data == null
|| provider.getAuthority() == null) {
continue;
}
StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";", false);
StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";",
false);
while (authorities.hasMoreElements()) {
if (Objects.equals(authorities.nextElement(), data.getAuthority())) {
if (queriesAuthorities.contains(authorities.nextToken())) {
return true;
}
}
}
return false;
}
private static boolean matchesIntentFilters(Intent intent, AndroidPackage potentialTarget) {
for (int s = ArrayUtils.size(potentialTarget.getServices()) - 1; s >= 0; s--) {
ParsedService service = potentialTarget.getServices().get(s);
if (!service.exported) {
@@ -368,8 +371,8 @@ public class AppsFilter {
final AndroidPackage existingPkg = existingSetting.pkg;
// let's evaluate the ability of already added packages to see this new package
if (!newIsForceQueryable) {
if (canQueryViaIntent(existingPkg, newPkg)) {
mQueriesViaIntent.add(existingSetting.appId, newPkgSetting.appId);
if (canQueryViaComponents(existingPkg, newPkg)) {
mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
}
if (canQueryViaPackage(existingPkg, newPkg)
|| canQueryAsInstaller(existingSetting, newPkg)) {
@@ -378,8 +381,8 @@ public class AppsFilter {
}
// now we'll evaluate our new package's ability to see existing packages
if (!mForceQueryable.contains(existingSetting.appId)) {
if (canQueryViaIntent(newPkg, existingPkg)) {
mQueriesViaIntent.add(newPkgSetting.appId, existingSetting.appId);
if (canQueryViaComponents(newPkg, existingPkg)) {
mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
}
if (canQueryViaPackage(newPkg, existingPkg)
|| canQueryAsInstaller(newPkgSetting, existingPkg)) {
@@ -427,9 +430,9 @@ public class AppsFilter {
}
}
mQueriesViaIntent.remove(setting.appId);
for (int i = mQueriesViaIntent.size() - 1; i >= 0; i--) {
mQueriesViaIntent.remove(mQueriesViaIntent.keyAt(i), setting.appId);
mQueriesViaComponent.remove(setting.appId);
for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
}
mQueriesViaPackage.remove(setting.appId);
for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
@@ -594,10 +597,10 @@ public class AppsFilter {
Trace.endSection();
}
try {
Trace.beginSection("mQueriesViaIntent");
if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
Trace.beginSection("mQueriesViaComponent");
if (mQueriesViaComponent.contains(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "queries intent");
log(callingSetting, targetPkgSetting, "queries component");
}
return false;
}
@@ -732,7 +735,7 @@ public class AppsFilter {
pw.println(" queries via package name:");
dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages);
pw.println(" queries via intent:");
dumpQueriesMap(pw, filteringAppId, mQueriesViaIntent, " ", expandPackages);
dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages);
pw.println(" queryable via interaction:");
for (int user : users) {
pw.append(" User ").append(Integer.toString(user)).println(":");

View File

@@ -35,7 +35,6 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
import android.content.pm.parsing.PackageImpl;
import android.content.pm.parsing.ParsingPackage;
import android.net.Uri;
import android.os.Build;
import android.os.Process;
import android.util.ArrayMap;
@@ -87,6 +86,17 @@ public class AppsFilterTest {
return pkg;
}
private static ParsingPackage pkgQueriesProvider(String packageName,
String... queriesAuthorities) {
ParsingPackage pkg = pkg(packageName);
if (queriesAuthorities != null) {
for (String authority : queriesAuthorities) {
pkg.addQueriesProvider(authority);
}
}
return pkg;
}
private static ParsingPackage pkg(String packageName, String... queriesPackages) {
ParsingPackage pkg = pkg(packageName);
if (queriesPackages != null) {
@@ -172,8 +182,7 @@ public class AppsFilterTest {
PackageSetting target = simulateAddPackage(appsFilter,
pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.some.other.package",
new Intent().setData(Uri.parse("content://com.some.authority"))),
pkgQueriesProvider("com.some.other.package", "com.some.authority"),
DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -188,8 +197,7 @@ public class AppsFilterTest {
PackageSetting target = simulateAddPackage(appsFilter,
pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.some.other.package",
new Intent().setData(Uri.parse("content://com.some.other.authority"))),
pkgQueriesProvider("com.some.other.package", "com.some.other.authority"),
DUMMY_CALLING_UID);
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -205,8 +213,7 @@ public class AppsFilterTest {
pkgWithProvider("com.some.package", "com.some.authority;com.some.other.authority"),
DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.some.other.package",
new Intent().setData(Uri.parse("content://com.some.authority"))),
pkgQueriesProvider("com.some.other.package", "com.some.authority"),
DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -266,7 +273,7 @@ public class AppsFilterTest {
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.some.other.package"), DUMMY_CALLING_UID);

View File

@@ -391,6 +391,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage);
manifest_action["queries"]["intent"] = intent_filter_action;
manifest_action["queries"]["provider"].Action(RequiredAndroidAttribute("authorities"));
// TODO: more complicated component name tag
manifest_action["key-sets"]["key-set"]["public-key"];