/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER; import static android.content.pm.PackageManagerInternal.PACKAGE_SETUP_WIZARD; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; import static com.android.server.pm.PackageManagerService.fixProcessName; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.InstantAppResolveInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageParser.ServiceIntentInfo; import android.content.pm.PackageUserState; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Log; import android.util.LogPrinter; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.IntentResolver; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** Resolves all Android component types [activities, services, providers and receivers]. */ public class ComponentResolver { private static final boolean DEBUG = false; private static final String TAG = "PackageManager"; private static final boolean DEBUG_FILTERS = false; private static final boolean DEBUG_SHOW_INFO = false; /** * The set of all protected actions [i.e. those actions for which a high priority * intent filter is disallowed]. */ private static final Set<String> PROTECTED_ACTIONS = new ArraySet<>(); static { PROTECTED_ACTIONS.add(Intent.ACTION_SEND); PROTECTED_ACTIONS.add(Intent.ACTION_SENDTO); PROTECTED_ACTIONS.add(Intent.ACTION_SEND_MULTIPLE); PROTECTED_ACTIONS.add(Intent.ACTION_VIEW); } static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> { int v1 = r1.priority; int v2 = r2.priority; //System.out.println("Comparing: q1=" + q1 + " q2=" + q2); if (v1 != v2) { return (v1 > v2) ? -1 : 1; } v1 = r1.preferredOrder; v2 = r2.preferredOrder; if (v1 != v2) { return (v1 > v2) ? -1 : 1; } if (r1.isDefault != r2.isDefault) { return r1.isDefault ? -1 : 1; } v1 = r1.match; v2 = r2.match; //System.out.println("Comparing: m1=" + m1 + " m2=" + m2); if (v1 != v2) { return (v1 > v2) ? -1 : 1; } if (r1.system != r2.system) { return r1.system ? -1 : 1; } if (r1.activityInfo != null) { return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName); } if (r1.serviceInfo != null) { return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName); } if (r1.providerInfo != null) { return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName); } return 0; }; private static UserManagerService sUserManager; private static PackageManagerInternal sPackageManagerInternal; /** * Locking within package manager is going to get worse before it gets better. Currently, * we need to share the {@link PackageManagerService} lock to prevent deadlocks. This occurs * because in order to safely query the resolvers, we need to obtain this lock. However, * during resolution, we call into the {@link PackageManagerService}. This is _not_ to * operate on data controlled by the service proper, but, to check the state of package * settings [contained in a {@link Settings} object]. However, the {@link Settings} object * happens to be protected by the main {@link PackageManagerService} lock. * <p> * There are a couple potential solutions. * <ol> * <li>Split all of our locks into reader/writer locks. This would allow multiple, * simultaneous read operations and means we don't have to be as cautious about lock * layering. Only when we want to perform a write operation will we ever be in a * position to deadlock the system.</li> * <li>Use the same lock across all classes within the {@code com.android.server.pm} * package. By unifying the lock object, we remove any potential lock layering issues * within the package manager. However, we already have a sense that this lock is * heavily contended and merely adding more dependencies on it will have further * impact.</li> * <li>Implement proper lock ordering within the package manager. By defining the * relative layer of the component [eg. {@link PackageManagerService} is at the top. * Somewhere in the middle would be {@link ComponentResolver}. At the very bottom * would be {@link Settings}.] The ordering would allow higher layers to hold their * lock while calling down. Lower layers must relinquish their lock before calling up. * Since {@link Settings} would live at the lowest layer, the {@link ComponentResolver} * would be able to hold its lock while checking the package setting state.</li> * </ol> */ private final Object mLock; /** All available activities, for your resolving pleasure. */ @GuardedBy("mLock") private final ActivityIntentResolver mActivities = new ActivityIntentResolver(); /** All available providers, for your resolving pleasure. */ @GuardedBy("mLock") private final ProviderIntentResolver mProviders = new ProviderIntentResolver(); /** All available receivers, for your resolving pleasure. */ @GuardedBy("mLock") private final ActivityIntentResolver mReceivers = new ActivityIntentResolver(); /** All available services, for your resolving pleasure. */ @GuardedBy("mLock") private final ServiceIntentResolver mServices = new ServiceIntentResolver(); /** Mapping from provider authority [first directory in content URI codePath) to provider. */ @GuardedBy("mLock") private final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = new ArrayMap<>(); /** Whether or not processing protected filters should be deferred. */ private boolean mDeferProtectedFilters = true; /** * Tracks high priority intent filters for protected actions. During boot, certain * filter actions are protected and should never be allowed to have a high priority * intent filter for them. However, there is one, and only one exception -- the * setup wizard. It must be able to define a high priority intent filter for these * actions to ensure there are no escapes from the wizard. We need to delay processing * of these during boot as we need to inspect at all of the intent filters on the * /system partition in order to know which component is the setup wizard. This can * only ever be non-empty if {@link #mDeferProtectedFilters} is {@code true}. */ private List<PackageParser.ActivityIntentInfo> mProtectedFilters; ComponentResolver(UserManagerService userManager, PackageManagerInternal packageManagerInternal, Object lock) { sPackageManagerInternal = packageManagerInternal; sUserManager = userManager; mLock = lock; } /** Returns the given activity */ PackageParser.Activity getActivity(ComponentName component) { synchronized (mLock) { return mActivities.mActivities.get(component); } } /** Returns the given provider */ PackageParser.Provider getProvider(ComponentName component) { synchronized (mLock) { return mProviders.mProviders.get(component); } } /** Returns the given receiver */ PackageParser.Activity getReceiver(ComponentName component) { synchronized (mLock) { return mReceivers.mActivities.get(component); } } /** Returns the given service */ PackageParser.Service getService(ComponentName component) { synchronized (mLock) { return mServices.mServices.get(component); } } List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags, int userId) { synchronized (mLock) { return mActivities.queryIntent(intent, resolvedType, flags, userId); } } List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags, List<PackageParser.Activity> activities, int userId) { synchronized (mLock) { return mActivities.queryIntentForPackage( intent, resolvedType, flags, activities, userId); } } List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags, int userId) { synchronized (mLock) { return mProviders.queryIntent(intent, resolvedType, flags, userId); } } List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags, List<PackageParser.Provider> providers, int userId) { synchronized (mLock) { return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId); } } List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, int flags, int userId) { if (!sUserManager.exists(userId)) { return null; } List<ProviderInfo> providerList = null; synchronized (mLock) { for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) { final PackageParser.Provider p = mProviders.mProviders.valueAt(i); final PackageSetting ps = (PackageSetting) p.owner.mExtras; if (ps == null) { continue; } if (p.info.authority == null) { continue; } if (processName != null && (!p.info.processName.equals(processName) || !UserHandle.isSameApp(p.info.applicationInfo.uid, uid))) { continue; } // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter. if (metaDataKey != null && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) { continue; } final ProviderInfo info = PackageParser.generateProviderInfo( p, flags, ps.readUserState(userId), userId); if (info == null) { continue; } if (providerList == null) { providerList = new ArrayList<>(i + 1); } providerList.add(info); } } return providerList; } ProviderInfo queryProvider(String authority, int flags, int userId) { synchronized (mLock) { final PackageParser.Provider p = mProvidersByAuthority.get(authority); if (p == null) { return null; } final PackageSetting ps = (PackageSetting) p.owner.mExtras; if (ps == null) { return null; } return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId), userId); } } void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode, int userId) { synchronized (mLock) { for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) { final PackageParser.Provider p = mProvidersByAuthority.valueAt(i); final PackageSetting ps = (PackageSetting) p.owner.mExtras; if (ps == null) { continue; } if (!p.syncable) { continue; } if (safeMode && (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { continue; } final ProviderInfo info = PackageParser.generateProviderInfo(p, 0, ps.readUserState(userId), userId); if (info == null) { continue; } outNames.add(mProvidersByAuthority.keyAt(i)); outInfo.add(info); } } } List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, int userId) { synchronized (mLock) { return mReceivers.queryIntent(intent, resolvedType, flags, userId); } } List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, List<PackageParser.Activity> receivers, int userId) { synchronized (mLock) { return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId); } } List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags, int userId) { synchronized (mLock) { return mServices.queryIntent(intent, resolvedType, flags, userId); } } List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags, List<PackageParser.Service> services, int userId) { synchronized (mLock) { return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId); } } /** Returns {@code true} if the given activity is defined by some package */ boolean isActivityDefined(ComponentName component) { synchronized (mLock) { return mActivities.mActivities.get(component) != null; } } /** Asserts none of the providers defined in the given package haven't already been defined. */ void assertProvidersNotDefined(PackageParser.Package pkg) throws PackageManagerException { synchronized (mLock) { assertProvidersNotDefinedLocked(pkg); } } /** Add all components defined in the given package to the internal structures. */ void addAllComponents(PackageParser.Package pkg, boolean chatty) { final ArrayList<PackageParser.ActivityIntentInfo> newIntents = new ArrayList<>(); synchronized (mLock) { addActivitiesLocked(pkg, newIntents, chatty); addReceiversLocked(pkg, chatty); addProvidersLocked(pkg, chatty); addServicesLocked(pkg, chatty); } final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName( PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM); for (int i = newIntents.size() - 1; i >= 0; --i) { final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i); final PackageParser.Package disabledPkg = sPackageManagerInternal .getDisabledSystemPackage(intentInfo.activity.info.packageName); final List<PackageParser.Activity> systemActivities = disabledPkg != null ? disabledPkg.activities : null; adjustPriority(systemActivities, intentInfo, setupWizardPackage); } } /** Removes all components defined in the given package from the internal structures. */ void removeAllComponents(PackageParser.Package pkg, boolean chatty) { synchronized (mLock) { removeAllComponentsLocked(pkg, chatty); } } /** * Reprocess any protected filters that have been deferred. At this point, we've scanned * all of the filters defined on the /system partition and know the special components. */ void fixProtectedFilterPriorities() { if (!mDeferProtectedFilters) { return; } mDeferProtectedFilters = false; if (mProtectedFilters == null || mProtectedFilters.size() == 0) { return; } final List<ActivityIntentInfo> protectedFilters = mProtectedFilters; mProtectedFilters = null; final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName( PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM); if (DEBUG_FILTERS && setupWizardPackage == null) { Slog.i(TAG, "No setup wizard;" + " All protected intents capped to priority 0"); } for (int i = protectedFilters.size() - 1; i >= 0; --i) { final ActivityIntentInfo filter = protectedFilters.get(i); if (filter.activity.info.packageName.equals(setupWizardPackage)) { if (DEBUG_FILTERS) { Slog.i(TAG, "Found setup wizard;" + " allow priority " + filter.getPriority() + ";" + " package: " + filter.activity.info.packageName + " activity: " + filter.activity.className + " priority: " + filter.getPriority()); } // skip setup wizard; allow it to keep the high priority filter continue; } if (DEBUG_FILTERS) { Slog.i(TAG, "Protected action; cap priority to 0;" + " package: " + filter.activity.info.packageName + " activity: " + filter.activity.className + " origPrio: " + filter.getPriority()); } filter.setPriority(0); } } void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) { if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:" : "Activity Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } } void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) { if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:" : "Provider Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } } void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) { if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } } void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) { if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:" : "Service Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } } void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) { boolean printedSomething = false; for (PackageParser.Provider p : mProviders.mProviders.values()) { if (packageName != null && !packageName.equals(p.info.packageName)) { continue; } if (!printedSomething) { if (dumpState.onTitlePrinted()) { pw.println(); } pw.println("Registered ContentProviders:"); printedSomething = true; } pw.print(" "); p.printComponentShortName(pw); pw.println(":"); pw.print(" "); pw.println(p.toString()); } printedSomething = false; for (Map.Entry<String, PackageParser.Provider> entry : mProvidersByAuthority.entrySet()) { PackageParser.Provider p = entry.getValue(); if (packageName != null && !packageName.equals(p.info.packageName)) { continue; } if (!printedSomething) { if (dumpState.onTitlePrinted()) { pw.println(); } pw.println("ContentProvider Authorities:"); printedSomething = true; } pw.print(" ["); pw.print(entry.getKey()); pw.println("]:"); pw.print(" "); pw.println(p.toString()); if (p.info != null && p.info.applicationInfo != null) { final String appInfo = p.info.applicationInfo.toString(); pw.print(" applicationInfo="); pw.println(appInfo); } } } void dumpServicePermissions(PrintWriter pw, DumpState dumpState, String packageName) { if (dumpState.onTitlePrinted()) pw.println(); pw.println("Service permissions:"); final Iterator<ServiceIntentInfo> filterIterator = mServices.filterIterator(); while (filterIterator.hasNext()) { final ServiceIntentInfo info = filterIterator.next(); final ServiceInfo serviceInfo = info.service.info; final String permission = serviceInfo.permission; if (permission != null) { pw.print(" "); pw.print(serviceInfo.getComponentName().flattenToShortString()); pw.print(": "); pw.println(permission); } } } @GuardedBy("mLock") private void addActivitiesLocked(PackageParser.Package pkg, List<PackageParser.ActivityIntentInfo> newIntents, boolean chatty) { final int activitiesSize = pkg.activities.size(); StringBuilder r = null; for (int i = 0; i < activitiesSize; i++) { PackageParser.Activity a = pkg.activities.get(i); a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName); mActivities.addActivity(a, "activity", newIntents); if (DEBUG_PACKAGE_SCANNING && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (DEBUG_PACKAGE_SCANNING && chatty) { Log.d(TAG, " Activities: " + (r == null ? "<NONE>" : r)); } } @GuardedBy("mLock") private void addProvidersLocked(PackageParser.Package pkg, boolean chatty) { final int providersSize = pkg.providers.size(); StringBuilder r = null; for (int i = 0; i < providersSize; i++) { PackageParser.Provider p = pkg.providers.get(i); p.info.processName = fixProcessName(pkg.applicationInfo.processName, p.info.processName); mProviders.addProvider(p); p.syncable = p.info.isSyncable; if (p.info.authority != null) { String[] names = p.info.authority.split(";"); p.info.authority = null; for (int j = 0; j < names.length; j++) { if (j == 1 && p.syncable) { // We only want the first authority for a provider to possibly be // syncable, so if we already added this provider using a different // authority clear the syncable flag. We copy the provider before // changing it because the mProviders object contains a reference // to a provider that we don't want to change. // Only do this for the second authority since the resulting provider // object can be the same for all future authorities for this provider. p = new PackageParser.Provider(p); p.syncable = false; } if (!mProvidersByAuthority.containsKey(names[j])) { mProvidersByAuthority.put(names[j], p); if (p.info.authority == null) { p.info.authority = names[j]; } else { p.info.authority = p.info.authority + ";" + names[j]; } if (DEBUG_PACKAGE_SCANNING && chatty) { Log.d(TAG, "Registered content provider: " + names[j] + ", className = " + p.info.name + ", isSyncable = " + p.info.isSyncable); } } else { final PackageParser.Provider other = mProvidersByAuthority.get(names[j]); final ComponentName component = (other != null && other.getComponentName() != null) ? other.getComponentName() : null; final String packageName = component != null ? component.getPackageName() : "?"; Slog.w(TAG, "Skipping provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + ")" + ": name already used by " + packageName); } } } if (DEBUG_PACKAGE_SCANNING && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(p.info.name); } } if (DEBUG_PACKAGE_SCANNING && chatty) { Log.d(TAG, " Providers: " + (r == null ? "<NONE>" : r)); } } @GuardedBy("mLock") private void addReceiversLocked(PackageParser.Package pkg, boolean chatty) { final int receiversSize = pkg.receivers.size(); StringBuilder r = null; for (int i = 0; i < receiversSize; i++) { PackageParser.Activity a = pkg.receivers.get(i); a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName); mReceivers.addActivity(a, "receiver", null); if (DEBUG_PACKAGE_SCANNING && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (DEBUG_PACKAGE_SCANNING && chatty) { Log.d(TAG, " Receivers: " + (r == null ? "<NONE>" : r)); } } @GuardedBy("mLock") private void addServicesLocked(PackageParser.Package pkg, boolean chatty) { final int servicesSize = pkg.services.size(); StringBuilder r = null; for (int i = 0; i < servicesSize; i++) { PackageParser.Service s = pkg.services.get(i); s.info.processName = fixProcessName(pkg.applicationInfo.processName, s.info.processName); mServices.addService(s); if (DEBUG_PACKAGE_SCANNING && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(s.info.name); } } if (DEBUG_PACKAGE_SCANNING && chatty) { Log.d(TAG, " Services: " + (r == null ? "<NONE>" : r)); } } /** * <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 static <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(); } } } } private static boolean isProtectedAction(ActivityIntentInfo filter) { final Iterator<String> actionsIter = filter.actionsIterator(); while (actionsIter != null && actionsIter.hasNext()) { final String filterAction = actionsIter.next(); if (PROTECTED_ACTIONS.contains(filterAction)) { return true; } } return false; } /** * Finds a privileged activity that matches the specified activity names. */ private static 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; } /** * Adjusts the priority of the given intent filter according to policy. * <p> * <ul> * <li>The priority for non privileged applications is capped to '0'</li> * <li>The priority for protected actions on privileged applications is capped to '0'</li> * <li>The priority for unbundled updates to privileged applications is capped to the * priority defined on the system partition</li> * </ul> * <p> * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is * allowed to obtain any priority on any action. */ private void adjustPriority(List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent, String setupWizardPackage) { // 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 privilegedApp = ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0); if (!privilegedApp) { // non-privileged applications can never define a priority >0 if (DEBUG_FILTERS) { Slog.i(TAG, "Non-privileged 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 if (isProtectedAction(intent)) { if (mDeferProtectedFilters) { // We can't deal with these just yet. No component should ever obtain a // >0 priority for a protected actions, with ONE exception -- the setup // wizard. The setup wizard, however, cannot be known until we're able to // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do // until all intent filters have been processed. Chicken, meet egg. // Let the filter temporarily have a high priority and rectify the // priorities after all system packages have been scanned. if (mProtectedFilters == null) { mProtectedFilters = new ArrayList<>(); } mProtectedFilters.add(intent); if (DEBUG_FILTERS) { Slog.i(TAG, "Protected action; save for later;" + " package: " + applicationInfo.packageName + " activity: " + intent.activity.className + " origPrio: " + intent.getPriority()); } return; } else { if (DEBUG_FILTERS && setupWizardPackage == null) { Slog.i(TAG, "No setup wizard;" + " All protected intents capped to priority 0"); } if (intent.activity.info.packageName.equals(setupWizardPackage)) { if (DEBUG_FILTERS) { Slog.i(TAG, "Found setup wizard;" + " allow priority " + intent.getPriority() + ";" + " package: " + intent.activity.info.packageName + " activity: " + intent.activity.className + " priority: " + intent.getPriority()); } // setup wizard gets whatever it wants return; } if (DEBUG_FILTERS) { Slog.i(TAG, "Protected action; cap priority to 0;" + " package: " + intent.activity.info.packageName + " activity: " + intent.activity.className + " origPrio: " + intent.getPriority()); } intent.setPriority(0); return; } } // privileged apps on the system image get whatever priority they request return; } // privileged 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<>(foundActivity.intents); final List<ActivityIntentInfo> foundFilters = mActivities.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 } @GuardedBy("mLock") private void removeAllComponentsLocked(PackageParser.Package pkg, boolean chatty) { int componentSize; StringBuilder r; int i; componentSize = pkg.activities.size(); r = null; for (i = 0; i < componentSize; i++) { PackageParser.Activity a = pkg.activities.get(i); mActivities.removeActivity(a, "activity"); if (DEBUG_REMOVE && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (DEBUG_REMOVE && chatty) { Log.d(TAG, " Activities: " + (r == null ? "<NONE>" : r)); } componentSize = pkg.providers.size(); r = null; for (i = 0; i < componentSize; i++) { PackageParser.Provider p = pkg.providers.get(i); mProviders.removeProvider(p); if (p.info.authority == null) { // Another content provider with this authority existed when this app was // installed, so this authority is null. Ignore it as we don't have to // unregister the provider. continue; } String[] names = p.info.authority.split(";"); for (int j = 0; j < names.length; j++) { if (mProvidersByAuthority.get(names[j]) == p) { mProvidersByAuthority.remove(names[j]); if (DEBUG_REMOVE && chatty) { Log.d(TAG, "Unregistered content provider: " + names[j] + ", className = " + p.info.name + ", isSyncable = " + p.info.isSyncable); } } } if (DEBUG_REMOVE && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(p.info.name); } } if (DEBUG_REMOVE && chatty) { Log.d(TAG, " Providers: " + (r == null ? "<NONE>" : r)); } componentSize = pkg.receivers.size(); r = null; for (i = 0; i < componentSize; i++) { PackageParser.Activity a = pkg.receivers.get(i); mReceivers.removeActivity(a, "receiver"); if (DEBUG_REMOVE && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (DEBUG_REMOVE && chatty) { Log.d(TAG, " Receivers: " + (r == null ? "<NONE>" : r)); } componentSize = pkg.services.size(); r = null; for (i = 0; i < componentSize; i++) { PackageParser.Service s = pkg.services.get(i); mServices.removeService(s); if (DEBUG_REMOVE && chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(s.info.name); } } if (DEBUG_REMOVE && chatty) { Log.d(TAG, " Services: " + (r == null ? "<NONE>" : r)); } } @GuardedBy("mLock") private void assertProvidersNotDefinedLocked(PackageParser.Package pkg) throws PackageManagerException { final int providersSize = pkg.providers.size(); int i; for (i = 0; i < providersSize; i++) { PackageParser.Provider p = pkg.providers.get(i); if (p.info.authority != null) { final String[] names = p.info.authority.split(";"); for (int j = 0; j < names.length; j++) { if (mProvidersByAuthority.containsKey(names[j])) { final PackageParser.Provider other = mProvidersByAuthority.get(names[j]); final String otherPackageName = (other != null && other.getComponentName() != null) ? other.getComponentName().getPackageName() : "?"; // if we're installing over the same already-installed package, this is ok if (!otherPackageName.equals(pkg.packageName)) { throw new PackageManagerException( INSTALL_FAILED_CONFLICTING_PROVIDER, "Can't install because provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + ") is already used by " + otherPackageName); } } } } } } private static final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { @Override public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { if (!sUserManager.exists(userId)) return null; mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0); return super.queryIntent(intent, resolvedType, defaultOnly, userId); } List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) { return null; } mFlags = flags; return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId); } List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags, List<PackageParser.Activity> packageActivities, int userId) { if (!sUserManager.exists(userId)) { return null; } if (packageActivities == null) { return null; } mFlags = flags; final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; final int activitiesSize = packageActivities.size(); ArrayList<PackageParser.ActivityIntentInfo[]> listCut = new ArrayList<>(activitiesSize); ArrayList<PackageParser.ActivityIntentInfo> intentFilters; for (int i = 0; i < activitiesSize; ++i) { intentFilters = packageActivities.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { PackageParser.ActivityIntentInfo[] array = new PackageParser.ActivityIntentInfo[intentFilters.size()]; intentFilters.toArray(array); listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); } private void addActivity(PackageParser.Activity a, String type, List<PackageParser.ActivityIntentInfo> newIntents) { mActivities.put(a.getComponentName(), a); if (DEBUG_SHOW_INFO) { final CharSequence label = a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name; Log.v(TAG, " " + type + " " + label + ":"); } if (DEBUG_SHOW_INFO) { Log.v(TAG, " Class=" + a.info.name); } final int intentsSize = a.intents.size(); for (int j = 0; j < intentsSize; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (newIntents != null && "activity".equals(type)) { newIntents.add(intent); } if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } if (!intent.debugCheck()) { Log.w(TAG, "==> For Activity " + a.info.name); } addFilter(intent); } } private void removeActivity(PackageParser.Activity a, String type) { mActivities.remove(a.getComponentName()); if (DEBUG_SHOW_INFO) { Log.v(TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); Log.v(TAG, " Class=" + a.info.name); } final int intentsSize = a.intents.size(); for (int j = 0; j < intentsSize; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } removeFilter(intent); } } @Override protected boolean allowFilterResult( PackageParser.ActivityIntentInfo filter, List<ResolveInfo> dest) { ActivityInfo filterAi = filter.activity.info; for (int i = dest.size() - 1; i >= 0; --i) { ActivityInfo destAi = dest.get(i).activityInfo; if (destAi.name == filterAi.name && destAi.packageName == filterAi.packageName) { return false; } } return true; } @Override protected ActivityIntentInfo[] newArray(int size) { return new ActivityIntentInfo[size]; } @Override protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.activity.owner; if (p != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (ps != null) { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0 && ps.getStopped(userId); } } return false; } @Override protected boolean isPackageForFilter(String packageName, PackageParser.ActivityIntentInfo info) { return packageName.equals(info.activity.owner.packageName); } private void log(String reason, ActivityIntentInfo info, int match, int userId) { Slog.w(TAG, reason + "; match: " + DebugUtils.flagsToString(IntentFilter.class, "MATCH_", match) + "; userId: " + userId + "; intent info: " + info); } @Override protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info, int match, int userId) { if (!sUserManager.exists(userId)) { if (DEBUG) { log("User doesn't exist", info, match, userId); } return null; } if (!sPackageManagerInternal.isEnabledAndMatches(info.activity.info, mFlags, userId)) { if (DEBUG) { log("!PackageManagerInternal.isEnabledAndMatches; mFlags=" + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags), info, match, userId); } return null; } final PackageParser.Activity activity = info.activity; PackageSetting ps = (PackageSetting) activity.owner.mExtras; if (ps == null) { if (DEBUG) { log("info.activity.owner.mExtras == null", info, match, userId); } return null; } final PackageUserState userState = ps.readUserState(userId); ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags, userState, userId); if (ai == null) { if (DEBUG) { log("Failed to create ActivityInfo based on " + info.activity, info, match, userId); } return null; } final boolean matchExplicitlyVisibleOnly = (mFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean matchVisibleToInstantApp = (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean componentVisible = matchVisibleToInstantApp && info.isVisibleToInstantApp() && (!matchExplicitlyVisibleOnly || info.isExplicitlyVisibleToInstantApp()); final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0; // throw out filters that aren't visible to ephemeral apps if (matchVisibleToInstantApp && !(componentVisible || userState.instantApp)) { if (DEBUG) { log("Filter(s) not visible to ephemeral apps" + "; matchVisibleToInstantApp=" + matchVisibleToInstantApp + "; matchInstantApp=" + matchInstantApp + "; info.isVisibleToInstantApp()=" + info.isVisibleToInstantApp() + "; matchExplicitlyVisibleOnly=" + matchExplicitlyVisibleOnly + "; info.isExplicitlyVisibleToInstantApp()=" + info.isExplicitlyVisibleToInstantApp(), info, match, userId); } return null; } // throw out instant app filters if we're not explicitly requesting them if (!matchInstantApp && userState.instantApp) { if (DEBUG) { log("Instant app filter is not explicitly requested", info, match, userId); } return null; } // throw out instant app filters if updates are available; will trigger // instant app resolution if (userState.instantApp && ps.isUpdateAvailable()) { if (DEBUG) { log("Instant app update is available", info, match, userId); } return null; } final ResolveInfo res = new ResolveInfo(); res.activityInfo = ai; if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = info; } res.handleAllWebDataURI = info.handleAllWebDataURI(); res.priority = info.getPriority(); res.preferredOrder = activity.owner.mPreferredOrder; //System.out.println("Result: " + res.activityInfo.className + // " = " + res.priority); res.match = match; res.isDefault = info.hasDefault; res.labelRes = info.labelRes; res.nonLocalizedLabel = info.nonLocalizedLabel; if (sPackageManagerInternal.userNeedsBadging(userId)) { res.noResourceId = true; } else { res.icon = info.icon; } res.iconResourceId = info.icon; res.system = res.activityInfo.applicationInfo.isSystemApp(); res.isInstantAppAvailable = userState.instantApp; return res; } @Override protected void sortResults(List<ResolveInfo> results) { results.sort(RESOLVE_PRIORITY_SORTER); } @Override protected void dumpFilter(PrintWriter out, String prefix, PackageParser.ActivityIntentInfo filter) { out.print(prefix); out.print(Integer.toHexString(System.identityHashCode(filter.activity))); out.print(' '); filter.activity.printComponentShortName(out); out.print(" filter "); out.println(Integer.toHexString(System.identityHashCode(filter))); } @Override protected Object filterToLabel(PackageParser.ActivityIntentInfo filter) { return filter.activity; } protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) { PackageParser.Activity activity = (PackageParser.Activity) label; out.print(prefix); out.print(Integer.toHexString(System.identityHashCode(activity))); out.print(' '); activity.printComponentShortName(out); if (count > 1) { out.print(" ("); out.print(count); out.print(" filters)"); } out.println(); } // Keys are String (activity class name), values are Activity. private final ArrayMap<ComponentName, PackageParser.Activity> mActivities = new ArrayMap<>(); private int mFlags; } private static final class ProviderIntentResolver extends IntentResolver<PackageParser.ProviderIntentInfo, ResolveInfo> { @Override public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; return super.queryIntent(intent, resolvedType, defaultOnly, userId); } List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) { return null; } mFlags = flags; return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId); } List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags, List<PackageParser.Provider> packageProviders, int userId) { if (!sUserManager.exists(userId)) { return null; } if (packageProviders == null) { return null; } mFlags = flags; final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; final int providersSize = packageProviders.size(); ArrayList<PackageParser.ProviderIntentInfo[]> listCut = new ArrayList<>(providersSize); ArrayList<PackageParser.ProviderIntentInfo> intentFilters; for (int i = 0; i < providersSize; ++i) { intentFilters = packageProviders.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { PackageParser.ProviderIntentInfo[] array = new PackageParser.ProviderIntentInfo[intentFilters.size()]; intentFilters.toArray(array); listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); } void addProvider(PackageParser.Provider p) { if (mProviders.containsKey(p.getComponentName())) { Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring"); return; } mProviders.put(p.getComponentName(), p); if (DEBUG_SHOW_INFO) { Log.v(TAG, " " + (p.info.nonLocalizedLabel != null ? p.info.nonLocalizedLabel : p.info.name) + ":"); Log.v(TAG, " Class=" + p.info.name); } final int intentsSize = p.intents.size(); int j; for (j = 0; j < intentsSize; j++) { PackageParser.ProviderIntentInfo intent = p.intents.get(j); if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } if (!intent.debugCheck()) { Log.w(TAG, "==> For Provider " + p.info.name); } addFilter(intent); } } void removeProvider(PackageParser.Provider p) { mProviders.remove(p.getComponentName()); if (DEBUG_SHOW_INFO) { Log.v(TAG, " " + (p.info.nonLocalizedLabel != null ? p.info.nonLocalizedLabel : p.info.name) + ":"); Log.v(TAG, " Class=" + p.info.name); } final int intentsSize = p.intents.size(); int j; for (j = 0; j < intentsSize; j++) { PackageParser.ProviderIntentInfo intent = p.intents.get(j); if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } removeFilter(intent); } } @Override protected boolean allowFilterResult( PackageParser.ProviderIntentInfo filter, List<ResolveInfo> dest) { ProviderInfo filterPi = filter.provider.info; for (int i = dest.size() - 1; i >= 0; i--) { ProviderInfo destPi = dest.get(i).providerInfo; if (destPi.name == filterPi.name && destPi.packageName == filterPi.packageName) { return false; } } return true; } @Override protected PackageParser.ProviderIntentInfo[] newArray(int size) { return new PackageParser.ProviderIntentInfo[size]; } @Override protected boolean isFilterStopped(PackageParser.ProviderIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) { return true; } PackageParser.Package p = filter.provider.owner; if (p != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (ps != null) { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0 && ps.getStopped(userId); } } return false; } @Override protected boolean isPackageForFilter(String packageName, PackageParser.ProviderIntentInfo info) { return packageName.equals(info.provider.owner.packageName); } @Override protected ResolveInfo newResult(PackageParser.ProviderIntentInfo filter, int match, int userId) { if (!sUserManager.exists(userId)) { return null; } final PackageParser.ProviderIntentInfo info = filter; if (!sPackageManagerInternal.isEnabledAndMatches(info.provider.info, mFlags, userId)) { return null; } final PackageParser.Provider provider = info.provider; PackageSetting ps = (PackageSetting) provider.owner.mExtras; if (ps == null) { return null; } final PackageUserState userState = ps.readUserState(userId); final boolean matchVisibleToInstantApp = (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0; // throw out filters that aren't visible to instant applications if (matchVisibleToInstantApp && !(info.isVisibleToInstantApp() || userState.instantApp)) { return null; } // throw out instant application filters if we're not explicitly requesting them if (!isInstantApp && userState.instantApp) { return null; } // throw out instant application filters if updates are available; will trigger // instant application resolution if (userState.instantApp && ps.isUpdateAvailable()) { return null; } ProviderInfo pi = PackageParser.generateProviderInfo(provider, mFlags, userState, userId); if (pi == null) { return null; } final ResolveInfo res = new ResolveInfo(); res.providerInfo = pi; if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = filter; } res.priority = info.getPriority(); res.preferredOrder = provider.owner.mPreferredOrder; res.match = match; res.isDefault = info.hasDefault; res.labelRes = info.labelRes; res.nonLocalizedLabel = info.nonLocalizedLabel; res.icon = info.icon; res.system = res.providerInfo.applicationInfo.isSystemApp(); return res; } @Override protected void sortResults(List<ResolveInfo> results) { results.sort(RESOLVE_PRIORITY_SORTER); } @Override protected void dumpFilter(PrintWriter out, String prefix, PackageParser.ProviderIntentInfo filter) { out.print(prefix); out.print(Integer.toHexString(System.identityHashCode(filter.provider))); out.print(' '); filter.provider.printComponentShortName(out); out.print(" filter "); out.println(Integer.toHexString(System.identityHashCode(filter))); } @Override protected Object filterToLabel(PackageParser.ProviderIntentInfo filter) { return filter.provider; } protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) { final PackageParser.Provider provider = (PackageParser.Provider) label; out.print(prefix); out.print(Integer.toHexString(System.identityHashCode(provider))); out.print(' '); provider.printComponentShortName(out); if (count > 1) { out.print(" ("); out.print(count); out.print(" filters)"); } out.println(); } private final ArrayMap<ComponentName, PackageParser.Provider> mProviders = new ArrayMap<>(); private int mFlags; } private static final class ServiceIntentResolver extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> { @Override public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; return super.queryIntent(intent, resolvedType, defaultOnly, userId); } List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; mFlags = flags; return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId); } List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags, List<PackageParser.Service> packageServices, int userId) { if (!sUserManager.exists(userId)) return null; if (packageServices == null) { return null; } mFlags = flags; final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; final int servicesSize = packageServices.size(); ArrayList<PackageParser.ServiceIntentInfo[]> listCut = new ArrayList<>(servicesSize); ArrayList<PackageParser.ServiceIntentInfo> intentFilters; for (int i = 0; i < servicesSize; ++i) { intentFilters = packageServices.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { PackageParser.ServiceIntentInfo[] array = new PackageParser.ServiceIntentInfo[intentFilters.size()]; intentFilters.toArray(array); listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); } void addService(PackageParser.Service s) { mServices.put(s.getComponentName(), s); if (DEBUG_SHOW_INFO) { Log.v(TAG, " " + (s.info.nonLocalizedLabel != null ? s.info.nonLocalizedLabel : s.info.name) + ":"); Log.v(TAG, " Class=" + s.info.name); } final int intentsSize = s.intents.size(); int j; for (j = 0; j < intentsSize; j++) { PackageParser.ServiceIntentInfo intent = s.intents.get(j); if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } if (!intent.debugCheck()) { Log.w(TAG, "==> For Service " + s.info.name); } addFilter(intent); } } void removeService(PackageParser.Service s) { mServices.remove(s.getComponentName()); if (DEBUG_SHOW_INFO) { Log.v(TAG, " " + (s.info.nonLocalizedLabel != null ? s.info.nonLocalizedLabel : s.info.name) + ":"); Log.v(TAG, " Class=" + s.info.name); } final int intentsSize = s.intents.size(); int j; for (j = 0; j < intentsSize; j++) { PackageParser.ServiceIntentInfo intent = s.intents.get(j); if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } removeFilter(intent); } } @Override protected boolean allowFilterResult( PackageParser.ServiceIntentInfo filter, List<ResolveInfo> dest) { ServiceInfo filterSi = filter.service.info; for (int i = dest.size() - 1; i >= 0; --i) { ServiceInfo destAi = dest.get(i).serviceInfo; if (destAi.name == filterSi.name && destAi.packageName == filterSi.packageName) { return false; } } return true; } @Override protected PackageParser.ServiceIntentInfo[] newArray(int size) { return new PackageParser.ServiceIntentInfo[size]; } @Override protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.service.owner; if (p != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (ps != null) { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0 && ps.getStopped(userId); } } return false; } @Override protected boolean isPackageForFilter(String packageName, PackageParser.ServiceIntentInfo info) { return packageName.equals(info.service.owner.packageName); } @Override protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter, int match, int userId) { if (!sUserManager.exists(userId)) return null; final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo) filter; if (!sPackageManagerInternal.isEnabledAndMatches(info.service.info, mFlags, userId)) { return null; } final PackageParser.Service service = info.service; PackageSetting ps = (PackageSetting) service.owner.mExtras; if (ps == null) { return null; } final PackageUserState userState = ps.readUserState(userId); ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags, userState, userId); if (si == null) { return null; } final boolean matchVisibleToInstantApp = (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0; // throw out filters that aren't visible to ephemeral apps if (matchVisibleToInstantApp && !(info.isVisibleToInstantApp() || userState.instantApp)) { return null; } // throw out ephemeral filters if we're not explicitly requesting them if (!isInstantApp && userState.instantApp) { return null; } // throw out instant app filters if updates are available; will trigger // instant app resolution if (userState.instantApp && ps.isUpdateAvailable()) { return null; } final ResolveInfo res = new ResolveInfo(); res.serviceInfo = si; if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = filter; } res.priority = info.getPriority(); res.preferredOrder = service.owner.mPreferredOrder; res.match = match; res.isDefault = info.hasDefault; res.labelRes = info.labelRes; res.nonLocalizedLabel = info.nonLocalizedLabel; res.icon = info.icon; res.system = res.serviceInfo.applicationInfo.isSystemApp(); return res; } @Override protected void sortResults(List<ResolveInfo> results) { results.sort(RESOLVE_PRIORITY_SORTER); } @Override protected void dumpFilter(PrintWriter out, String prefix, PackageParser.ServiceIntentInfo filter) { out.print(prefix); out.print(Integer.toHexString(System.identityHashCode(filter.service))); out.print(' '); filter.service.printComponentShortName(out); out.print(" filter "); out.print(Integer.toHexString(System.identityHashCode(filter))); if (filter.service.info.permission != null) { out.print(" permission "); out.println(filter.service.info.permission); } else { out.println(); } } @Override protected Object filterToLabel(PackageParser.ServiceIntentInfo filter) { return filter.service; } protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) { final PackageParser.Service service = (PackageParser.Service) label; out.print(prefix); out.print(Integer.toHexString(System.identityHashCode(service))); out.print(' '); service.printComponentShortName(out); if (count > 1) { out.print(" ("); out.print(count); out.print(" filters)"); } out.println(); } // Keys are String (activity class name), values are Activity. private final ArrayMap<ComponentName, PackageParser.Service> mServices = new ArrayMap<>(); private int mFlags; } static final class InstantAppIntentResolver extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter, AuxiliaryResolveInfo.AuxiliaryFilter> { /** * The result that has the highest defined order. Ordering applies on a * per-package basis. Mapping is from package name to Pair of order and * EphemeralResolveInfo. * <p> * NOTE: This is implemented as a field variable for convenience and efficiency. * By having a field variable, we're able to track filter ordering as soon as * a non-zero order is defined. Otherwise, multiple loops across the result set * would be needed to apply ordering. If the intent resolver becomes re-entrant, * this needs to be contained entirely within {@link #filterResults}. */ final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult = new ArrayMap<>(); @Override protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) { return new AuxiliaryResolveInfo.AuxiliaryFilter[size]; } @Override protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo.AuxiliaryFilter responseObj) { return true; } @Override protected AuxiliaryResolveInfo.AuxiliaryFilter newResult( AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) { if (!sUserManager.exists(userId)) { return null; } final String packageName = responseObj.resolveInfo.getPackageName(); final Integer order = responseObj.getOrder(); final Pair<Integer, InstantAppResolveInfo> lastOrderResult = mOrderResult.get(packageName); // ordering is enabled and this item's order isn't high enough if (lastOrderResult != null && lastOrderResult.first >= order) { return null; } final InstantAppResolveInfo res = responseObj.resolveInfo; if (order > 0) { // non-zero order, enable ordering mOrderResult.put(packageName, new Pair<>(order, res)); } return responseObj; } @Override protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) { // only do work if ordering is enabled [most of the time it won't be] if (mOrderResult.size() == 0) { return; } int resultSize = results.size(); for (int i = 0; i < resultSize; i++) { final InstantAppResolveInfo info = results.get(i).resolveInfo; final String packageName = info.getPackageName(); final Pair<Integer, InstantAppResolveInfo> savedInfo = mOrderResult.get(packageName); if (savedInfo == null) { // package doesn't having ordering continue; } if (savedInfo.second == info) { // circled back to the highest ordered item; remove from order list mOrderResult.remove(packageName); if (mOrderResult.size() == 0) { // no more ordered items break; } continue; } // item has a worse order, remove it from the result list results.remove(i); resultSize--; i--; } } } /** Generic to create an {@link Iterator} for a data type */ static class IterGenerator<E> { public Iterator<E> generate(ActivityIntentInfo info) { return null; } } /** Create an {@link Iterator} for intent actions */ static class ActionIterGenerator extends IterGenerator<String> { @Override public Iterator<String> generate(ActivityIntentInfo info) { return info.actionsIterator(); } } /** Create an {@link Iterator} for intent categories */ static class CategoriesIterGenerator extends IterGenerator<String> { @Override public Iterator<String> generate(ActivityIntentInfo info) { return info.categoriesIterator(); } } /** Create an {@link Iterator} for intent schemes */ static class SchemesIterGenerator extends IterGenerator<String> { @Override public Iterator<String> generate(ActivityIntentInfo info) { return info.schemesIterator(); } } /** Create an {@link Iterator} for intent authorities */ static class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> { @Override public Iterator<IntentFilter.AuthorityEntry> generate(ActivityIntentInfo info) { return info.authoritiesIterator(); } } }