diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-08-16 11:44:40 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2014-08-16 19:34:13 -0700 |
commit | f2ac2761276e4972f6463d6818c9f5798bdc9a4d (patch) | |
tree | 6af12a5b070e7f079877e848ace16d408a7bd132 /services/core | |
parent | 82d6d337b389ef088879a5e527d44c75c41c5b44 (diff) | |
download | frameworks_base-f2ac2761276e4972f6463d6818c9f5798bdc9a4d.zip frameworks_base-f2ac2761276e4972f6463d6818c9f5798bdc9a4d.tar.gz frameworks_base-f2ac2761276e4972f6463d6818c9f5798bdc9a4d.tar.bz2 |
Fix issue #17082301: replacePreferredActivity is ignoring userId
It was being given the argument and just... ignoring it.
But the bulk of this change is to make replacePreferredActivity
better about replacing -- it now detects if the request will not
make a change and, in that case, just do nothing.
The reason for this?
It turns out that each time you install an app, the telephony
system is calling this function over 20 times to set the default
SMS app. This is almost always doing nothing, but before this
change it means we would re-write packages.xml over 20 times...!
There are definitely more improvements that can be made here (delaying
write of packages.xml to allow them to batch together, reducing
the amount of calls being made), but until then this is a big
improvement.
Change-Id: I02c4235b8ecd5c13ef53e65d13c7dc2223719cec
Diffstat (limited to 'services/core')
3 files changed, 205 insertions, 27 deletions
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 64b0487..07cc864 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<>(); + } + 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<>(); + } + 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 89bd1d4..2d18343 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2920,7 +2920,8 @@ public class PackageManagerService extends IPackageManager.Stub { findPreferredActivity(intent, resolvedType, flags, query, 0, false, true, false, userId); // Add the new activity as the last chosen for this filter - addPreferredActivityInternal(filter, match, null, activity, false, userId); + addPreferredActivityInternal(filter, match, null, activity, false, userId, + "Setting last chosen"); } @Override @@ -11462,11 +11463,13 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { - addPreferredActivityInternal(filter, match, set, activity, true, userId); + addPreferredActivityInternal(filter, match, set, activity, true, userId, + "Adding preferred"); } private void addPreferredActivityInternal(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity, boolean always, int userId) { + ComponentName[] set, ComponentName activity, boolean always, int userId, + String opname) { // writer int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, "add preferred activity"); @@ -11488,10 +11491,11 @@ public class PackageManagerService extends IPackageManager.Stub { android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :"); + PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId); + Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user " + + userId + ":"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); - mSettings.editPreferredActivitiesLPw(userId).addFilter( - new PreferredActivity(filter, match, set, activity, always)); + pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); mSettings.writePackageRestrictionsLPr(userId); } } @@ -11514,7 +11518,6 @@ public class PackageManagerService extends IPackageManager.Stub { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, "replace preferred activity"); - final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) @@ -11529,30 +11532,61 @@ public class PackageManagerService extends IPackageManager.Stub { android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - PreferredIntentResolver pir = mSettings.mPreferredActivities.get(callingUserId); + PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); if (pir != null) { - Intent intent = new Intent(filter.getAction(0)).addCategory(filter.getCategory(0)); - if (filter.countDataSchemes() == 1) { - Uri.Builder builder = new Uri.Builder(); - builder.scheme(filter.getDataScheme(0)); - intent.setData(builder.build()); - } - List<PreferredActivity> matches = pir.queryIntent( - intent, null, true, callingUserId); + // Get all of the existing entries that exactly match this filter. + ArrayList<PreferredActivity> existing = pir.findFilters(filter); + if (existing != null && existing.size() == 1) { + PreferredActivity cur = existing.get(0); + if (DEBUG_PREFERRED) { + Slog.i(TAG, "Checking replace of preferred:"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + if (!cur.mPref.mAlways) { + Slog.i(TAG, " -- CUR; not mAlways!"); + } else { + Slog.i(TAG, " -- CUR: mMatch=" + cur.mPref.mMatch); + Slog.i(TAG, " -- CUR: mSet=" + + Arrays.toString(cur.mPref.mSetComponents)); + Slog.i(TAG, " -- CUR: mComponent=" + cur.mPref.mShortComponent); + Slog.i(TAG, " -- NEW: mMatch=" + + (match&IntentFilter.MATCH_CATEGORY_MASK)); + Slog.i(TAG, " -- CUR: mSet=" + Arrays.toString(set)); + Slog.i(TAG, " -- CUR: mComponent=" + activity.flattenToShortString()); + } + } + if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity) + && cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK) + && cur.mPref.sameSet(set)) { + if (DEBUG_PREFERRED) { + Slog.i(TAG, "Replacing with same preferred activity " + + cur.mPref.mShortComponent + " for user " + + userId + ":"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + } else { + Slog.i(TAG, "Replacing with same preferred activity " + + cur.mPref.mShortComponent + " for user " + + userId); + } + return; + } + } + if (DEBUG_PREFERRED) { - Slog.i(TAG, matches.size() + " preferred matches for " + intent); + Slog.i(TAG, existing.size() + " existing preferred matches for:"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - for (int i = 0; i < matches.size(); i++) { - PreferredActivity pa = matches.get(i); + for (int i = 0; i < existing.size(); i++) { + PreferredActivity pa = existing.get(i); if (DEBUG_PREFERRED) { - Slog.i(TAG, "Removing preferred activity " + Slog.i(TAG, "Removing existing preferred activity " + pa.mPref.mComponent + ":"); - filter.dump(new LogPrinter(Log.INFO, TAG), " "); + pa.dump(new LogPrinter(Log.INFO, TAG), " "); } pir.removeFilter(pa); } } - addPreferredActivityInternal(filter, match, set, activity, true, callingUserId); + addPreferredActivityInternal(filter, match, set, activity, true, userId, + "Replacing preferred"); } } diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index f437372..69c1909 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -44,10 +44,10 @@ public class PreferredComponent { // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. public boolean mAlways; - private final String[] mSetPackages; - private final String[] mSetClasses; - private final String[] mSetComponents; - private final String mShortComponent; + final String[] mSetPackages; + final String[] mSetClasses; + final String[] mSetComponents; + final String mShortComponent; private String mParseError; private final Callbacks mCallbacks; @@ -193,7 +193,12 @@ public class PreferredComponent { } public boolean sameSet(List<ResolveInfo> query, int priority) { - if (mSetPackages == null) return false; + if (mSetPackages == null) { + return query == null; + } + if (query == null) { + return false; + } final int NQ = query.size(); final int NS = mSetPackages.length; int numMatch = 0; @@ -215,6 +220,27 @@ public class PreferredComponent { return numMatch == NS; } + public boolean sameSet(ComponentName[] comps) { + if (mSetPackages == null) return false; + final int NQ = comps.length; + final int NS = mSetPackages.length; + int numMatch = 0; + for (int i=0; i<NQ; i++) { + ComponentName cn = comps[i]; + boolean good = false; + for (int j=0; j<NS; j++) { + if (mSetPackages[j].equals(cn.getPackageName()) + && mSetClasses[j].equals(cn.getClassName())) { + numMatch++; + good = true; + break; + } + } + if (!good) return false; + } + return numMatch == NS; + } + public void dump(PrintWriter out, String prefix, Object ident) { out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(ident))); |