diff options
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:"); |