blob: 560ca92f494dc8c82a15663b86e77e3e0e5ddf9f [file] [log] [blame]
/*
* 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.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 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
.getDisabledPackage(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 != 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);
}
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
if (!sUserManager.exists(userId)) return null;
if (!sPackageManagerInternal.isEnabledAndMatches(info.activity.info, mFlags, userId)) {
return null;
}
final PackageParser.Activity activity = info.activity;
PackageSetting ps = (PackageSetting) activity.owner.mExtras;
if (ps == null) {
return null;
}
final PackageUserState userState = ps.readUserState(userId);
ActivityInfo ai =
PackageParser.generateActivityInfo(activity, mFlags, userState, userId);
if (ai == null) {
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)) {
return null;
}
// throw out instant app filters if we're not explicitly requesting them
if (!matchInstantApp && 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.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();
}
}
}