summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/IntentFilter.java74
-rw-r--r--services/core/java/com/android/server/IntentResolver.java118
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java260
3 files changed, 447 insertions, 5 deletions
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index cd4a7a0ecb7d..14178ab1b3b6 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -564,6 +564,11 @@ public class IntentFilter implements Parcelable {
return mDataTypes != null && findMimeType(type);
}
+ /** @hide */
+ public final boolean hasExactDataType(String type) {
+ return mDataTypes != null && mDataTypes.contains(type);
+ }
+
/**
* Return the number of data types in the filter.
*/
@@ -681,6 +686,29 @@ public class IntentFilter implements Parcelable {
return mPort;
}
+ /** @hide */
+ public boolean match(AuthorityEntry other) {
+ if (mWild != other.mWild) {
+ return false;
+ }
+ if (!mHost.equals(other.mHost)) {
+ return false;
+ }
+ if (mPort != other.mPort) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AuthorityEntry) {
+ final AuthorityEntry other = (AuthorityEntry)obj;
+ return match(other);
+ }
+ return false;
+ }
+
/**
* Determine whether this AuthorityEntry matches the given data Uri.
* <em>Note that this comparison is case-sensitive, unlike formal
@@ -715,7 +743,7 @@ public class IntentFilter implements Parcelable {
}
return MATCH_CATEGORY_HOST;
}
- };
+ }
/**
* Add a new Intent data "scheme specific part" to match against. The filter must
@@ -792,6 +820,21 @@ public class IntentFilter implements Parcelable {
return false;
}
+ /** @hide */
+ public final boolean hasDataSchemeSpecificPart(PatternMatcher ssp) {
+ if (mDataSchemeSpecificParts == null) {
+ return false;
+ }
+ final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
+ for (int i = 0; i < numDataSchemeSpecificParts; i++) {
+ final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
+ if (pe.getType() == ssp.getType() && pe.getPath().equals(ssp.getPath())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Return an iterator over the filter's data scheme specific parts.
*/
@@ -860,6 +903,20 @@ public class IntentFilter implements Parcelable {
return matchDataAuthority(data) >= 0;
}
+ /** @hide */
+ public final boolean hasDataAuthority(AuthorityEntry auth) {
+ if (mDataAuthorities == null) {
+ return false;
+ }
+ final int numDataAuthorities = mDataAuthorities.size();
+ for (int i = 0; i < numDataAuthorities; i++) {
+ if (mDataAuthorities.get(i).match(auth)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Return an iterator over the filter's data authorities.
*/
@@ -942,6 +999,21 @@ public class IntentFilter implements Parcelable {
return false;
}
+ /** @hide */
+ public final boolean hasDataPath(PatternMatcher path) {
+ if (mDataPaths == null) {
+ return false;
+ }
+ final int numDataPaths = mDataPaths.size();
+ for (int i = 0; i < numDataPaths; i++) {
+ final PatternMatcher pe = mDataPaths.get(i);
+ if (pe.getType() == path.getType() && pe.getPath().equals(path.getPath())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Return an iterator over the filter's data paths.
*/
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 64b0487fa320..d6dc4d59fab6 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -69,6 +69,124 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
}
+ private boolean filterEquals(IntentFilter f1, IntentFilter f2) {
+ int s1 = f1.countActions();
+ int s2 = f2.countActions();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasAction(f1.getAction(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countCategories();
+ s2 = f2.countCategories();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasCategory(f1.getCategory(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataTypes();
+ s2 = f2.countDataTypes();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasExactDataType(f1.getDataType(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataSchemes();
+ s2 = f2.countDataSchemes();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataScheme(f1.getDataScheme(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataAuthorities();
+ s2 = f2.countDataAuthorities();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataPaths();
+ s2 = f2.countDataPaths();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataPath(f1.getDataPath(i))) {
+ return false;
+ }
+ }
+ s1 = f1.countDataSchemeSpecificParts();
+ s2 = f2.countDataSchemeSpecificParts();
+ if (s1 != s2) {
+ return false;
+ }
+ for (int i=0; i<s1; i++) {
+ if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private ArrayList<F> collectFilters(F[] array, IntentFilter matching) {
+ ArrayList<F> res = null;
+ if (array != null) {
+ for (int i=0; i<array.length; i++) {
+ F cur = array[i];
+ if (cur == null) {
+ break;
+ }
+ if (filterEquals(cur, matching)) {
+ if (res == null) {
+ res = new ArrayList<F>();
+ }
+ res.add(cur);
+ }
+ }
+ }
+ return res;
+ }
+
+ public ArrayList<F> findFilters(IntentFilter matching) {
+ if (matching.countDataSchemes() == 1) {
+ // Fast case.
+ return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching);
+ } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) {
+ // Another fast case.
+ return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching);
+ } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0
+ && matching.countActions() == 1) {
+ // Last fast case.
+ return collectFilters(mActionToFilter.get(matching.getAction(0)), matching);
+ } else {
+ ArrayList<F> res = null;
+ for (F cur : mFilters) {
+ if (filterEquals(cur, matching)) {
+ if (res == null) {
+ res = new ArrayList<F>();
+ }
+ res.add(cur);
+ }
+ }
+ return res;
+ }
+ }
+
public void removeFilter(F f) {
removeFilterInternal(f);
mFilters.remove(f);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0f2bea68d98d..c892a2310254 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -191,6 +191,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_APP_DIR_OBSERVER = false;
private static final boolean DEBUG_VERIFY = false;
+ private static final boolean DEBUG_FILTERS = false;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -5726,6 +5727,255 @@ public class PackageManagerService extends IPackageManager.Stub {
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
}
+ /**
+ * Finds a privileged activity that matches the specified activity names.
+ */
+ private PackageParser.Activity findMatchingActivity(
+ List<PackageParser.Activity> activityList, ActivityInfo activityInfo) {
+ for (PackageParser.Activity sysActivity : activityList) {
+ if (sysActivity.info.name.equals(activityInfo.name)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.name.equals(activityInfo.targetActivity)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.targetActivity != null) {
+ if (sysActivity.info.targetActivity.equals(activityInfo.name)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.targetActivity.equals(activityInfo.targetActivity)) {
+ return sysActivity;
+ }
+ }
+ }
+ return null;
+ }
+
+ public class IterGenerator<E> {
+ public Iterator<E> generate(ActivityIntentInfo info) {
+ return null;
+ }
+ }
+
+ public class ActionIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.actionsIterator();
+ }
+ }
+
+ public class CategoriesIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.categoriesIterator();
+ }
+ }
+
+ public class SchemesIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.schemesIterator();
+ }
+ }
+
+ public class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> {
+ @Override
+ public Iterator<IntentFilter.AuthorityEntry> generate(ActivityIntentInfo info) {
+ return info.authoritiesIterator();
+ }
+ }
+
+ /**
+ * <em>WARNING</em> for performance reasons, the passed in intentList WILL BE
+ * MODIFIED. Do not pass in a list that should not be changed.
+ */
+ private <T> void getIntentListSubset(List<ActivityIntentInfo> intentList,
+ IterGenerator<T> generator, Iterator<T> searchIterator) {
+ // loop through the set of actions; every one must be found in the intent filter
+ while (searchIterator.hasNext()) {
+ // we must have at least one filter in the list to consider a match
+ if (intentList.size() == 0) {
+ break;
+ }
+
+ final T searchAction = searchIterator.next();
+
+ // loop through the set of intent filters
+ final Iterator<ActivityIntentInfo> intentIter = intentList.iterator();
+ while (intentIter.hasNext()) {
+ final ActivityIntentInfo intentInfo = intentIter.next();
+ boolean selectionFound = false;
+
+ // loop through the intent filter's selection criteria; at least one
+ // of them must match the searched criteria
+ final Iterator<T> intentSelectionIter = generator.generate(intentInfo);
+ while (intentSelectionIter != null && intentSelectionIter.hasNext()) {
+ final T intentSelection = intentSelectionIter.next();
+ if (intentSelection != null && intentSelection.equals(searchAction)) {
+ selectionFound = true;
+ break;
+ }
+ }
+
+ // the selection criteria wasn't found in this filter's set; this filter
+ // is not a potential match
+ if (!selectionFound) {
+ intentIter.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Adjusts the priority of the given intent filter according to policy.
+ * <p>
+ * <ul>
+ * <li>The priority for unbundled updates to system applications is capped to the
+ * priority defined on the system partition</li>
+ * </ul>
+ */
+ private void adjustPriority(
+ List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent) {
+ // nothing to do; priority is fine as-is
+ if (intent.getPriority() <= 0) {
+ return;
+ }
+
+ final ActivityInfo activityInfo = intent.activity.info;
+ final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
+
+ final boolean systemApp = isSystemApp(applicationInfo);
+ if (!systemApp) {
+ // non-system applications can never define a priority >0
+ Slog.w(TAG, "Non-system app; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ intent.setPriority(0);
+ return;
+ }
+
+ if (systemActivities == null) {
+ // the system package is not disabled; we're parsing the system partition
+ // apps on the system image get whatever priority they request
+ return;
+ }
+
+ // system app unbundled update ... try to find the same activity
+ final PackageParser.Activity foundActivity =
+ findMatchingActivity(systemActivities, activityInfo);
+ if (foundActivity == null) {
+ // this is a new activity; it cannot obtain >0 priority
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "New activity; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+
+ // found activity, now check for filter equivalence
+
+ // a shallow copy is enough; we modify the list, not its contents
+ final List<ActivityIntentInfo> intentListCopy =
+ new ArrayList<ActivityIntentInfo>(foundActivity.intents);
+ final List<ActivityIntentInfo> foundFilters = findFilters(intent);
+
+ // find matching action subsets
+ final Iterator<String> actionsIterator = intent.actionsIterator();
+ if (actionsIterator != null) {
+ getIntentListSubset(
+ intentListCopy, new ActionIterGenerator(), actionsIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched action; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching category subsets
+ final Iterator<String> categoriesIterator = intent.categoriesIterator();
+ if (categoriesIterator != null) {
+ getIntentListSubset(intentListCopy, new CategoriesIterGenerator(),
+ categoriesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched category; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching schemes subsets
+ final Iterator<String> schemesIterator = intent.schemesIterator();
+ if (schemesIterator != null) {
+ getIntentListSubset(intentListCopy, new SchemesIterGenerator(),
+ schemesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched scheme; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching authorities subsets
+ final Iterator<IntentFilter.AuthorityEntry>
+ authoritiesIterator = intent.authoritiesIterator();
+ if (authoritiesIterator != null) {
+ getIntentListSubset(intentListCopy,
+ new AuthoritiesIterGenerator(),
+ authoritiesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched authority; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // we found matching filter(s); app gets the max priority of all intents
+ int cappedPriority = 0;
+ for (int i = intentListCopy.size() - 1; i >= 0; --i) {
+ cappedPriority = Math.max(cappedPriority, intentListCopy.get(i).getPriority());
+ }
+ if (intent.getPriority() > cappedPriority) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found matching filter(s);"
+ + " cap priority to " + cappedPriority + ";"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(cappedPriority);
+ return;
+ }
+ // all this for nothing; the requested priority was <= what was on the system
+ }
+
public final void addActivity(PackageParser.Activity a, String type) {
final boolean systemApp = isSystemApp(a.info.applicationInfo);
mActivities.put(a.getComponentName(), a);
@@ -5738,10 +5988,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
- if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
- intent.setPriority(0);
- Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
- + a.className + " with priority > 0, forcing to 0");
+ if ("activity".equals(type)) {
+ final PackageSetting ps =
+ mSettings.getDisabledSystemPkgLPr(intent.activity.info.packageName);
+ final List<PackageParser.Activity> systemActivities =
+ ps != null && ps.pkg != null ? ps.pkg.activities : null;
+ adjustPriority(systemActivities, intent);
}
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");