/*
|
* 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();
|
}
|
}
|
|
}
|