diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 140098da57911..a0654ac0f2eb7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2132,6 +2132,13 @@ public class PackageManagerService extends IPackageManager.Stub boolean filterAppAccess(String packageName, int callingUid, int userId); @LiveImplementation(override = LiveImplementation.MANDATORY) void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) + FindPreferredActivityBodyResult findPreferredActivityInternal(Intent intent, + String resolvedType, int flags, List query, boolean always, + boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) + ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags, + List query, boolean debug, int userId); } /** @@ -2914,7 +2921,24 @@ public class PackageManagerService extends IPackageManager.Stub } allHomeCandidates.addAll(resolveInfos); - final String packageName = mDefaultAppProvider.getDefaultHome(userId); + String packageName = mDefaultAppProvider.getDefaultHome(userId); + if (packageName == null) { + // Role changes are not and cannot be atomic because its implementation lives inside + // a system app, so when the home role changes, there is a window when the previous + // role holder is removed and the new role holder is granted the preferred activity, + // but hasn't become the role holder yet. However, this case may be easily hit + // because the preferred activity change triggers a broadcast and receivers may try + // to get the default home activity there. So we need to fix it for this time + // window, and an easy workaround is to fallback to the current preferred activity. + final int appId = UserHandle.getAppId(Binder.getCallingUid()); + final boolean filtered = appId >= Process.FIRST_APPLICATION_UID; + FindPreferredActivityBodyResult result = findPreferredActivityInternal( + intent, null, 0, resolveInfos, true, false, false, userId, filtered); + ResolveInfo preferredResolveInfo = result.mPreferredResolveInfo; + if (preferredResolveInfo != null && preferredResolveInfo.activityInfo != null) { + packageName = preferredResolveInfo.activityInfo.packageName; + } + } if (packageName == null) { return null; } @@ -4846,6 +4870,284 @@ public class PackageManagerService extends IPackageManager.Stub } } // switch } + + // The body of findPreferredActivity. + protected FindPreferredActivityBodyResult findPreferredActivityBody( + Intent intent, String resolvedType, int flags, + List query, boolean always, + boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered, + int callingUid, boolean isDeviceProvisioned) { + FindPreferredActivityBodyResult result = new FindPreferredActivityBodyResult(); + + flags = updateFlagsForResolve( + flags, userId, callingUid, false /*includeInstantApps*/, + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, + resolvedType, flags)); + intent = updateIntentForResolve(intent); + + // Try to find a matching persistent preferred activity. + result.mPreferredResolveInfo = findPersistentPreferredActivityLP(intent, + resolvedType, flags, query, debug, userId); + + // If a persistent preferred activity matched, use it. + if (result.mPreferredResolveInfo != null) { + return result; + } + + PreferredIntentResolver pir = mSettings.getPreferredActivities(userId); + // Get the list of preferred activities that handle the intent + if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities..."); + List prefs = pir != null + ? pir.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId) + : null; + if (prefs != null && prefs.size() > 0) { + + // First figure out how good the original match set is. + // We will only allow preferred activities that came + // from the same match quality. + int match = 0; + + if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match..."); + + final int N = query.size(); + for (int j = 0; j < N; j++) { + final ResolveInfo ri = query.get(j); + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Match for " + ri.activityInfo + + ": 0x" + Integer.toHexString(match)); + } + if (ri.match > match) { + match = ri.match; + } + } + + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Best match: 0x" + Integer.toHexString(match)); + } + match &= IntentFilter.MATCH_CATEGORY_MASK; + final int M = prefs.size(); + for (int i = 0; i < M; i++) { + final PreferredActivity pa = prefs.get(i); + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Checking PreferredActivity ds=" + + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "") + + "\n component=" + pa.mPref.mComponent); + pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + } + if (pa.mPref.mMatch != match) { + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Skipping bad match " + + Integer.toHexString(pa.mPref.mMatch)); + } + continue; + } + // If it's not an "always" type preferred activity and that's what we're + // looking for, skip it. + if (always && !pa.mPref.mAlways) { + if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry"); + continue; + } + final ActivityInfo ai = getActivityInfo( + pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS + | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + userId); + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Found preferred activity:"); + if (ai != null) { + ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + } else { + Slog.v(TAG, " null"); + } + } + final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) + && !isDeviceProvisioned; + final boolean allowSetMutation = !excludeSetupWizardHomeActivity + && !queryMayBeFiltered; + if (ai == null) { + // Do not remove launcher's preferred activity during SetupWizard + // due to it may not install yet + if (!allowSetMutation) { + continue; + } + + // This previously registered preferred activity + // component is no longer known. Most likely an update + // to the app was installed and in the new version this + // component no longer exists. Clean it up by removing + // it from the preferred activities list, and skip it. + Slog.w(TAG, "Removing dangling preferred activity: " + + pa.mPref.mComponent); + pir.removeFilter(pa); + result.mChanged = true; + continue; + } + for (int j = 0; j < N; j++) { + final ResolveInfo ri = query.get(j); + if (!ri.activityInfo.applicationInfo.packageName + .equals(ai.applicationInfo.packageName)) { + continue; + } + if (!ri.activityInfo.name.equals(ai.name)) { + continue; + } + + if (removeMatches && allowSetMutation) { + pir.removeFilter(pa); + result.mChanged = true; + if (DEBUG_PREFERRED) { + Slog.v(TAG, "Removing match " + pa.mPref.mComponent); + } + break; + } + + // Okay we found a previously set preferred or last chosen app. + // If the result set is different from when this + // was created, and is not a subset of the preferred set, we need to + // clear it and re-ask the user their preference, if we're looking for + // an "always" type entry. + + if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) { + if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) { + if (allowSetMutation) { + // some components of the set are no longer present in + // the query, but the preferred activity can still be reused + if (DEBUG_PREFERRED) { + Slog.i(TAG, "Result set changed, but PreferredActivity" + + " is still valid as only non-preferred" + + " components were removed for " + intent + + " type " + resolvedType); + } + // remove obsolete components and re-add the up-to-date + // filter + PreferredActivity freshPa = new PreferredActivity(pa, + pa.mPref.mMatch, + pa.mPref.discardObsoleteComponents(query), + pa.mPref.mComponent, + pa.mPref.mAlways); + pir.removeFilter(pa); + pir.addFilter(freshPa); + result.mChanged = true; + } else { + if (DEBUG_PREFERRED) { + Slog.i(TAG, "Do not remove preferred activity"); + } + } + } else { + if (allowSetMutation) { + Slog.i(TAG, + "Result set changed, dropping preferred activity " + + "for " + intent + " type " + + resolvedType); + if (DEBUG_PREFERRED) { + Slog.v(TAG, + "Removing preferred activity since set changed " + + pa.mPref.mComponent); + } + pir.removeFilter(pa); + // Re-add the filter as a "last chosen" entry (!always) + PreferredActivity lastChosen = new PreferredActivity( + pa, pa.mPref.mMatch, null, pa.mPref.mComponent, + false); + pir.addFilter(lastChosen); + result.mChanged = true; + } + result.mPreferredResolveInfo = null; + return result; + } + } + + // Yay! Either the set matched or we're looking for the last chosen + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Returning preferred activity: " + + ri.activityInfo.packageName + "/" + ri.activityInfo.name); + } + result.mPreferredResolveInfo = ri; + return result; + } + } + } + return result; + } + + public final FindPreferredActivityBodyResult findPreferredActivityInternal( + Intent intent, String resolvedType, int flags, + List query, boolean always, + boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) { + + final int callingUid = Binder.getCallingUid(); + // Do NOT hold the packages lock; this calls up into the settings provider which + // could cause a deadlock. + final boolean isDeviceProvisioned = + android.provider.Settings.Global.getInt(mContext.getContentResolver(), + android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; + // Find the preferred activity - the lock is held inside the method. + return findPreferredActivityBody( + intent, resolvedType, flags, query, always, removeMatches, debug, + userId, queryMayBeFiltered, callingUid, isDeviceProvisioned); + } + + public final ResolveInfo findPersistentPreferredActivityLP(Intent intent, + String resolvedType, + int flags, List query, boolean debug, int userId) { + final int N = query.size(); + PersistentPreferredIntentResolver ppir = + mSettings.getPersistentPreferredActivities(userId); + // Get the list of persistent preferred activities that handle the intent + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Looking for persistent preferred activities..."); + } + List pprefs = ppir != null + ? ppir.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId) + : null; + if (pprefs != null && pprefs.size() > 0) { + final int M = pprefs.size(); + for (int i = 0; i < M; i++) { + final PersistentPreferredActivity ppa = pprefs.get(i); + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Checking PersistentPreferredActivity ds=" + + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "") + + "\n component=" + ppa.mComponent); + ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + } + final ActivityInfo ai = getActivityInfo(ppa.mComponent, + flags | MATCH_DISABLED_COMPONENTS, userId); + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Found persistent preferred activity:"); + if (ai != null) { + ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + } else { + Slog.v(TAG, " null"); + } + } + if (ai == null) { + // This previously registered persistent preferred activity + // component is no longer known. Ignore it and do NOT remove it. + continue; + } + for (int j = 0; j < N; j++) { + final ResolveInfo ri = query.get(j); + if (!ri.activityInfo.applicationInfo.packageName + .equals(ai.applicationInfo.packageName)) { + continue; + } + if (!ri.activityInfo.name.equals(ai.name)) { + continue; + } + // Found a persistent preference that can handle the intent. + if (DEBUG_PREFERRED || debug) { + Slog.v(TAG, "Returning persistent preferred activity: " + + ri.activityInfo.packageName + "/" + ri.activityInfo.name); + } + return ri; + } + } + } + return null; + } } /** @@ -5005,6 +5307,16 @@ public class PackageManagerService extends IPackageManager.Stub super.dump(type, fd, pw, dumpState); } } + public final FindPreferredActivityBodyResult findPreferredActivityBody(Intent intent, + String resolvedType, int flags, List query, boolean always, + boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered, + int callingUid, boolean isDeviceProvisioned) { + synchronized (mLock) { + return super.findPreferredActivityBody(intent, resolvedType, flags, query, always, + removeMatches, debug, userId, queryMayBeFiltered, callingUid, + isDeviceProvisioned); + } + } } /** @@ -5572,6 +5884,28 @@ public class PackageManagerService extends IPackageManager.Stub current.release(); } } + public final FindPreferredActivityBodyResult findPreferredActivityInternal(Intent intent, + String resolvedType, int flags, List query, boolean always, + boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) { + ThreadComputer current = live(); + try { + return current.mComputer.findPreferredActivityInternal(intent, resolvedType, flags, + query, always, removeMatches, debug, userId, queryMayBeFiltered); + } finally { + current.release(); + } + } + public final ResolveInfo findPersistentPreferredActivityLP(Intent intent, + String resolvedType, int flags, List query, boolean debug, + int userId) { + ThreadComputer current = live(); + try { + return current.mComputer.findPersistentPreferredActivityLP(intent, resolvedType, + flags, query, debug, userId); + } finally { + current.release(); + } + } } @@ -9068,7 +9402,7 @@ public class PackageManagerService extends IPackageManager.Stub /** * Update given intent when being used to request {@link ResolveInfo}. */ - private Intent updateIntentForResolve(Intent intent) { + private static Intent updateIntentForResolve(Intent intent) { if (intent.getSelector() != null) { intent = intent.getSelector(); } @@ -10240,7 +10574,7 @@ public class PackageManagerService extends IPackageManager.Stub userId); // Find any earlier preferred or last chosen entries and nuke them findPreferredActivityNotLocked( - intent, resolvedType, flags, query, 0, false, true, false, userId); + intent, resolvedType, flags, query, false, true, false, userId); // Add the new activity as the last chosen for this filter addPreferredActivity(filter, match, null, activity, false, userId, "Setting last chosen", false); @@ -10256,7 +10590,7 @@ public class PackageManagerService extends IPackageManager.Stub final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); return findPreferredActivityNotLocked( - intent, resolvedType, flags, query, 0, false, false, false, userId); + intent, resolvedType, flags, query, false, false, false, userId); } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, @@ -10298,7 +10632,7 @@ public class PackageManagerService extends IPackageManager.Stub // If we have saved a preference for a preferred activity for // this Intent, use that. ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType, - flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered); + flags, query, true, false, debug, userId, queryMayBeFiltered); if (ri != null) { return ri; } @@ -10411,287 +10745,72 @@ public class PackageManagerService extends IPackageManager.Stub } @GuardedBy("mLock") - private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, + private ResolveInfo findPersistentPreferredActivityLP(Intent intent, + String resolvedType, int flags, List query, boolean debug, int userId) { - final int N = query.size(); - PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId); - // Get the list of persistent preferred activities that handle the intent - if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities..."); - List pprefs = ppir != null - ? ppir.queryIntent(intent, resolvedType, - (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, - userId) - : null; - if (pprefs != null && pprefs.size() > 0) { - final int M = pprefs.size(); - for (int i=0; i") - + "\n component=" + ppa.mComponent); - ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); - } - final ActivityInfo ai = getActivityInfo(ppa.mComponent, - flags | MATCH_DISABLED_COMPONENTS, userId); - if (DEBUG_PREFERRED || debug) { - Slog.v(TAG, "Found persistent preferred activity:"); - if (ai != null) { - ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); - } else { - Slog.v(TAG, " null"); - } - } - if (ai == null) { - // This previously registered persistent preferred activity - // component is no longer known. Ignore it and do NOT remove it. - continue; - } - for (int j=0; j query, boolean always, + boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) { + return mComputer.findPreferredActivityInternal( + intent, resolvedType, flags, + query, always, + removeMatches, debug, userId, queryMayBeFiltered); + } + ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, - List query, int priority, boolean always, + List query, boolean always, boolean removeMatches, boolean debug, int userId) { return findPreferredActivityNotLocked( - intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId, + intent, resolvedType, flags, query, always, removeMatches, debug, userId, UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID); } // TODO: handle preferred activities missing while user has amnesia /** must not hold {@link #mLock} */ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, - List query, int priority, boolean always, + List query, boolean always, boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) { if (Thread.holdsLock(mLock)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mLock", new Throwable()); } if (!mUserManager.exists(userId)) return null; - final int callingUid = Binder.getCallingUid(); - // Do NOT hold the packages lock; this calls up into the settings provider which - // could cause a deadlock. - final boolean isDeviceProvisioned = - android.provider.Settings.Global.getInt(mContext.getContentResolver(), - android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; - flags = updateFlagsForResolve( - flags, userId, callingUid, false /*includeInstantApps*/, - isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, - flags)); - intent = updateIntentForResolve(intent); - // writer - synchronized (mLock) { - // Try to find a matching persistent preferred activity. - ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query, - debug, userId); - // If a persistent preferred activity matched, use it. - if (pri != null) { - return pri; + FindPreferredActivityBodyResult body = findPreferredActivityInternal( + intent, resolvedType, flags, query, always, + removeMatches, debug, userId, queryMayBeFiltered); + if (body.mChanged) { + if (DEBUG_PREFERRED) { + Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions"); } - - PreferredIntentResolver pir = mSettings.getPreferredActivities(userId); - // Get the list of preferred activities that handle the intent - if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities..."); - List prefs = pir != null - ? pir.queryIntent(intent, resolvedType, - (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, - userId) - : null; - if (prefs != null && prefs.size() > 0) { - boolean changed = false; - try { - // First figure out how good the original match set is. - // We will only allow preferred activities that came - // from the same match quality. - int match = 0; - - if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match..."); - - final int N = query.size(); - for (int j=0; j match) { - match = ri.match; - } - } - - if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Best match: 0x" - + Integer.toHexString(match)); - - match &= IntentFilter.MATCH_CATEGORY_MASK; - final int M = prefs.size(); - for (int i=0; i") - + "\n component=" + pa.mPref.mComponent); - pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); - } - if (pa.mPref.mMatch != match) { - if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping bad match " - + Integer.toHexString(pa.mPref.mMatch)); - continue; - } - // If it's not an "always" type preferred activity and that's what we're - // looking for, skip it. - if (always && !pa.mPref.mAlways) { - if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry"); - continue; - } - final ActivityInfo ai = getActivityInfo( - pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS - | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, - userId); - if (DEBUG_PREFERRED || debug) { - Slog.v(TAG, "Found preferred activity:"); - if (ai != null) { - ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); - } else { - Slog.v(TAG, " null"); - } - } - final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) - && !isDeviceProvisioned; - final boolean allowSetMutation = !excludeSetupWizardHomeActivity - && !queryMayBeFiltered; - if (ai == null) { - // Do not remove launcher's preferred activity during SetupWizard - // due to it may not install yet - if (!allowSetMutation) { - continue; - } - - // This previously registered preferred activity - // component is no longer known. Most likely an update - // to the app was installed and in the new version this - // component no longer exists. Clean it up by removing - // it from the preferred activities list, and skip it. - Slog.w(TAG, "Removing dangling preferred activity: " - + pa.mPref.mComponent); - pir.removeFilter(pa); - changed = true; - continue; - } - for (int j=0; j resolveInfos = queryIntentActivitiesInternal(intent, null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked( - intent, null, 0, resolveInfos, 0, true, false, false, userId); + intent, null, 0, resolveInfos, true, false, false, userId); final String packageName = preferredResolveInfo != null && preferredResolveInfo.activityInfo != null ? preferredResolveInfo.activityInfo.packageName : null;