diff options
-rw-r--r-- | api/current.txt | 5 | ||||
-rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 16 | ||||
-rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 3 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageManager.java | 18 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 29 | ||||
-rw-r--r-- | core/java/android/content/pm/ProviderInfo.java | 9 | ||||
-rw-r--r-- | core/java/android/content/pm/ResolveInfo.java | 60 | ||||
-rw-r--r-- | core/java/android/provider/DocumentsContract.java | 6 | ||||
-rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/RootsCache.java | 81 | ||||
-rw-r--r-- | packages/ExternalStorageProvider/AndroidManifest.xml | 12 | ||||
-rwxr-xr-x | services/java/com/android/server/pm/PackageManagerService.java | 273 | ||||
-rw-r--r-- | test-runner/src/android/test/mock/MockPackageManager.java | 12 |
12 files changed, 444 insertions, 80 deletions
diff --git a/api/current.txt b/api/current.txt index 548433e..9251d39 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7255,6 +7255,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int); method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int); method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName, android.content.Intent[], android.content.Intent, int); + method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent, int); method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent, int); method public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract deprecated void removePackageFromPreferred(java.lang.String); @@ -7420,6 +7421,7 @@ package android.content.pm { ctor public ProviderInfo(); ctor public ProviderInfo(android.content.pm.ProviderInfo); method public int describeContents(); + method public void dump(android.util.Printer, java.lang.String); field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000 field public java.lang.String authority; @@ -7453,6 +7455,7 @@ package android.content.pm { field public java.lang.CharSequence nonLocalizedLabel; field public int preferredOrder; field public int priority; + field public android.content.pm.ProviderInfo providerInfo; field public java.lang.String resolvePackageName; field public android.content.pm.ServiceInfo serviceInfo; field public int specificIndex; @@ -20982,6 +20985,7 @@ package android.provider { field public static final java.lang.String EXTRA_ERROR = "error"; field public static final java.lang.String EXTRA_INFO = "info"; field public static final java.lang.String EXTRA_LOADING = "loading"; + field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER"; } public static final class DocumentsContract.Document { @@ -24477,6 +24481,7 @@ package android.test.mock { method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int); method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int); method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName, android.content.Intent[], android.content.Intent, int); + method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent, int); method public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent, int); method public java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public void removePackageFromPreferred(java.lang.String); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 55c6672..b505d4f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -585,6 +585,22 @@ final class ApplicationPackageManager extends PackageManager { } @Override + public List<ResolveInfo> queryIntentContentProvidersAsUser( + Intent intent, int flags, int userId) { + try { + return mPM.queryIntentContentProviders(intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) { + return queryIntentContentProvidersAsUser(intent, flags, mContext.getUserId()); + } + + @Override public ProviderInfo resolveContentProvider(String name, int flags) { try { diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index acd4ffa..267fb2a 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -123,6 +123,9 @@ interface IPackageManager { List<ResolveInfo> queryIntentServices(in Intent intent, String resolvedType, int flags, int userId); + List<ResolveInfo> queryIntentContentProviders(in Intent intent, + String resolvedType, int flags, int userId); + /** * This implements getInstalledPackages via a "last returned row" * mechanism that is not exposed in the API. This is to get around the IPC diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b63db8a..8b8c58b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2210,6 +2210,24 @@ public abstract class PackageManager { public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId); + /** {@hide} */ + public abstract List<ResolveInfo> queryIntentContentProvidersAsUser( + Intent intent, int flags, int userId); + + /** + * Retrieve all providers that can match the given intent. + * + * @param intent An intent containing all of the desired specification + * (action, data, type, category, and/or component). + * @param flags Additional option flags. + * @return A List<ResolveInfo> containing one entry for each matching + * ProviderInfo. These are ordered from best to worst match. If + * there are no matching providers, an empty list is returned. + * @see #GET_INTENT_FILTERS + * @see #GET_RESOLVED_FILTER + */ + public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags); + /** * Find a single content provider by its base path name. * diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b489ee9..17d13e5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2819,7 +2819,14 @@ public class PackageParser { continue; } - if (parser.getName().equals("meta-data")) { + if (parser.getName().equals("intent-filter")) { + ProviderIntentInfo intent = new ProviderIntentInfo(outInfo); + if (!parseIntent(res, parser, attrs, true, intent, outError)) { + return false; + } + outInfo.intents.add(intent); + + } else if (parser.getName().equals("meta-data")) { if ((outInfo.metaData=parseMetaData(res, parser, attrs, outInfo.metaData, outError)) == null) { return false; @@ -3982,7 +3989,7 @@ public class PackageParser { return si; } - public final static class Provider extends Component { + public final static class Provider extends Component<ProviderIntentInfo> { public final ProviderInfo info; public boolean syncable; @@ -4116,6 +4123,24 @@ public class PackageParser { } } + public static final class ProviderIntentInfo extends IntentInfo { + public final Provider provider; + + public ProviderIntentInfo(Provider provider) { + this.provider = provider; + } + + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("ProviderIntentInfo{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(' '); + provider.appendComponentShortName(sb); + sb.append('}'); + return sb.toString(); + } + } + /** * @hide */ diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java index a534176..f6ea058 100644 --- a/core/java/android/content/pm/ProviderInfo.java +++ b/core/java/android/content/pm/ProviderInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; import android.os.PatternMatcher; +import android.util.Printer; /** * Holds information about a specific @@ -112,7 +113,13 @@ public final class ProviderInfo extends ComponentInfo flags = orig.flags; isSyncable = orig.isSyncable; } - + + public void dump(Printer pw, String prefix) { + super.dumpFront(pw, prefix); + pw.println(prefix + "authority=" + authority); + pw.println(prefix + "flags=0x" + Integer.toHexString(flags)); + } + public int describeContents() { return 0; } diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index e360e40..1ff41c0 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -23,6 +23,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Printer; +import android.util.Slog; import java.text.Collator; import java.util.Comparator; @@ -34,20 +35,30 @@ import java.util.Comparator; * <intent> tags. */ public class ResolveInfo implements Parcelable { + private static final String TAG = "ResolveInfo"; + /** - * The activity or broadcast receiver that corresponds to this resolution match, - * if this resolution is for an activity or broadcast receiver. One and only one of this and - * serviceInfo must be non-null. + * The activity or broadcast receiver that corresponds to this resolution + * match, if this resolution is for an activity or broadcast receiver. + * Exactly one of {@link #activityInfo}, {@link #serviceInfo}, or + * {@link #providerInfo} will be non-null. */ public ActivityInfo activityInfo; /** - * The service that corresponds to this resolution match, if this - * resolution is for a service. One and only one of this and - * activityInfo must be non-null. + * The service that corresponds to this resolution match, if this resolution + * is for a service. Exactly one of {@link #activityInfo}, + * {@link #serviceInfo}, or {@link #providerInfo} will be non-null. */ public ServiceInfo serviceInfo; - + + /** + * The provider that corresponds to this resolution match, if this + * resolution is for a provider. Exactly one of {@link #activityInfo}, + * {@link #serviceInfo}, or {@link #providerInfo} will be non-null. + */ + public ProviderInfo providerInfo; + /** * The IntentFilter that was matched for this ResolveInfo. */ @@ -120,6 +131,13 @@ public class ResolveInfo implements Parcelable { */ public boolean system; + private ComponentInfo getComponentInfo() { + if (activityInfo != null) return activityInfo; + if (serviceInfo != null) return serviceInfo; + if (providerInfo != null) return providerInfo; + throw new IllegalStateException("Missing ComponentInfo!"); + } + /** * Retrieve the current textual label associated with this resolution. This * will call back on the given PackageManager to load the label from @@ -142,7 +160,7 @@ public class ResolveInfo implements Parcelable { return label.toString().trim(); } } - ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo; + ComponentInfo ci = getComponentInfo(); ApplicationInfo ai = ci.applicationInfo; if (labelRes != 0) { label = pm.getText(ci.packageName, labelRes, ai); @@ -176,7 +194,7 @@ public class ResolveInfo implements Parcelable { return dr; } } - ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo; + ComponentInfo ci = getComponentInfo(); ApplicationInfo ai = ci.applicationInfo; if (icon != 0) { dr = pm.getDrawable(ci.packageName, icon, ai); @@ -196,8 +214,8 @@ public class ResolveInfo implements Parcelable { */ public final int getIconResource() { if (icon != 0) return icon; - if (activityInfo != null) return activityInfo.getIconResource(); - if (serviceInfo != null) return serviceInfo.getIconResource(); + final ComponentInfo ci = getComponentInfo(); + if (ci != null) return ci.getIconResource(); return 0; } @@ -225,6 +243,9 @@ public class ResolveInfo implements Parcelable { } else if (serviceInfo != null) { pw.println(prefix + "ServiceInfo:"); serviceInfo.dump(pw, prefix + " "); + } else if (providerInfo != null) { + pw.println(prefix + "ProviderInfo:"); + providerInfo.dump(pw, prefix + " "); } } @@ -234,6 +255,7 @@ public class ResolveInfo implements Parcelable { public ResolveInfo(ResolveInfo orig) { activityInfo = orig.activityInfo; serviceInfo = orig.serviceInfo; + providerInfo = orig.providerInfo; filter = orig.filter; priority = orig.priority; preferredOrder = orig.preferredOrder; @@ -247,7 +269,7 @@ public class ResolveInfo implements Parcelable { } public String toString() { - ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo; + final ComponentInfo ci = getComponentInfo(); StringBuilder sb = new StringBuilder(128); sb.append("ResolveInfo{"); sb.append(Integer.toHexString(System.identityHashCode(this))); @@ -278,6 +300,9 @@ public class ResolveInfo implements Parcelable { } else if (serviceInfo != null) { dest.writeInt(2); serviceInfo.writeToParcel(dest, parcelableFlags); + } else if (providerInfo != null) { + dest.writeInt(3); + providerInfo.writeToParcel(dest, parcelableFlags); } else { dest.writeInt(0); } @@ -309,18 +334,21 @@ public class ResolveInfo implements Parcelable { }; private ResolveInfo(Parcel source) { + activityInfo = null; + serviceInfo = null; + providerInfo = null; switch (source.readInt()) { case 1: activityInfo = ActivityInfo.CREATOR.createFromParcel(source); - serviceInfo = null; break; case 2: serviceInfo = ServiceInfo.CREATOR.createFromParcel(source); - activityInfo = null; + break; + case 3: + providerInfo = ProviderInfo.CREATOR.createFromParcel(source); break; default: - activityInfo = null; - serviceInfo = null; + Slog.w(TAG, "Missing ComponentInfo!"); break; } if (source.readInt() != 0) { diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 631a8d4..1c14c38 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -70,8 +70,14 @@ public final class DocumentsContract { } /** {@hide} */ + @Deprecated public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER"; + /** + * Intent action used to identify {@link DocumentsProvider} instances. + */ + public static final String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER"; + /** {@hide} */ public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT"; /** {@hide} */ diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index bad0a96..eb56765 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -21,9 +21,11 @@ import static com.android.documentsui.DocumentsActivity.TAG; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; @@ -158,6 +160,9 @@ public class RootsCache { private class UpdateTask extends AsyncTask<Void, Void, Void> { private final String mFilterPackage; + private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create(); + private final HashSet<String> mTaskStoppedAuthorities = Sets.newHashSet(); + /** * Update all roots. */ @@ -177,54 +182,64 @@ public class RootsCache { protected Void doInBackground(Void... params) { final long start = SystemClock.elapsedRealtime(); - final Multimap<String, RootInfo> roots = ArrayListMultimap.create(); - final HashSet<String> stoppedAuthorities = Sets.newHashSet(); - - roots.put(mRecentsRoot.authority, mRecentsRoot); + mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot); final ContentResolver resolver = mContext.getContentResolver(); final PackageManager pm = mContext.getPackageManager(); - final List<ProviderInfo> providers = pm.queryContentProviders( + + // Pick up provider with action string + final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE); + final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0); + for (ResolveInfo info : providers) { + handleDocumentsProvider(info.providerInfo); + } + + // Pick up legacy providers + final List<ProviderInfo> legacyProviders = pm.queryContentProviders( null, -1, PackageManager.GET_META_DATA); - for (ProviderInfo info : providers) { + for (ProviderInfo info : legacyProviders) { if (info.metaData != null && info.metaData.containsKey( DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) { - // Ignore stopped packages for now; we might query them - // later during UI interaction. - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) { - if (LOGD) Log.d(TAG, "Ignoring stopped authority " + info.authority); - stoppedAuthorities.add(info.authority); - continue; - } - - // Try using cached roots if filtering - boolean cacheHit = false; - if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) { - synchronized (mLock) { - if (roots.putAll(info.authority, mRoots.get(info.authority))) { - if (LOGD) Log.d(TAG, "Used cached roots for " + info.authority); - cacheHit = true; - } - } - } - - // Cache miss, or loading everything - if (!cacheHit) { - roots.putAll( - info.authority, loadRootsForAuthority(resolver, info.authority)); - } + handleDocumentsProvider(info); } } final long delta = SystemClock.elapsedRealtime() - start; - Log.d(TAG, "Update found " + roots.size() + " roots in " + delta + "ms"); + Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms"); synchronized (mLock) { - mStoppedAuthorities = stoppedAuthorities; - mRoots = roots; + mRoots = mTaskRoots; + mStoppedAuthorities = mTaskStoppedAuthorities; } mFirstLoad.countDown(); return null; } + + private void handleDocumentsProvider(ProviderInfo info) { + // Ignore stopped packages for now; we might query them + // later during UI interaction. + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) { + if (LOGD) Log.d(TAG, "Ignoring stopped authority " + info.authority); + mTaskStoppedAuthorities.add(info.authority); + return; + } + + // Try using cached roots if filtering + boolean cacheHit = false; + if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) { + synchronized (mLock) { + if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) { + if (LOGD) Log.d(TAG, "Used cached roots for " + info.authority); + cacheHit = true; + } + } + } + + // Cache miss, or loading everything + if (!cacheHit) { + mTaskRoots.putAll(info.authority, + loadRootsForAuthority(mContext.getContentResolver(), info.authority)); + } + } } /** diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml index 7094efc..99a4260 100644 --- a/packages/ExternalStorageProvider/AndroidManifest.xml +++ b/packages/ExternalStorageProvider/AndroidManifest.xml @@ -11,9 +11,9 @@ android:grantUriPermissions="true" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS"> - <meta-data - android:name="android.content.DOCUMENT_PROVIDER" - android:value="true" /> + <intent-filter> + <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> + </intent-filter> </provider> <!-- TODO: find a better place for tests to live --> @@ -24,9 +24,9 @@ android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS" android:enabled="false"> - <meta-data - android:name="android.content.DOCUMENT_PROVIDER" - android:value="true" /> + <intent-filter> + <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> + </intent-filter> </provider> </application> </manifest> diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 4710f17..2a93dfc 100755 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -383,13 +383,12 @@ public class PackageManagerService extends IPackageManager.Stub { // All available services, for your resolving pleasure. final ServiceIntentResolver mServices = new ServiceIntentResolver(); - // Keys are String (provider class name), values are Provider. - final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent = - new HashMap<ComponentName, PackageParser.Provider>(); + // All available providers, for your resolving pleasure. + final ProviderIntentResolver mProviders = new ProviderIntentResolver(); // Mapping from provider base names (first directory in content URI codePath) // to the provider information. - final HashMap<String, PackageParser.Provider> mProviders = + final HashMap<String, PackageParser.Provider> mProvidersByAuthority = new HashMap<String, PackageParser.Provider>(); // Mapping from instrumentation class names to info about them. @@ -2095,7 +2094,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (!sUserManager.exists(userId)) return null; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get provider info"); synchronized (mPackages) { - PackageParser.Provider p = mProvidersByComponent.get(component); + PackageParser.Provider p = mProviders.mProviders.get(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getProviderInfo " + component + ": " + p); if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) { @@ -3121,6 +3120,43 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public List<ResolveInfo> queryIntentContentProviders( + Intent intent, String resolvedType, int flags, int userId) { + if (!sUserManager.exists(userId)) return Collections.emptyList(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } + if (comp != null) { + final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + final ProviderInfo pi = getProviderInfo(comp, flags, userId); + if (pi != null) { + final ResolveInfo ri = new ResolveInfo(); + ri.providerInfo = pi; + list.add(ri); + } + return list; + } + + // reader + synchronized (mPackages) { + String pkgName = intent.getPackage(); + if (pkgName == null) { + return mProviders.queryIntent(intent, resolvedType, flags, userId); + } + final PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return mProviders.queryIntentForPackage( + intent, resolvedType, flags, pkg.providers, userId); + } + return null; + } + } + + @Override public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) { final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; @@ -3293,7 +3329,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (!sUserManager.exists(userId)) return null; // reader synchronized (mPackages) { - final PackageParser.Provider provider = mProviders.get(name); + final PackageParser.Provider provider = mProvidersByAuthority.get(name); PackageSetting ps = provider != null ? mSettings.mPackages.get(provider.owner.packageName) : null; @@ -3314,8 +3350,8 @@ public class PackageManagerService extends IPackageManager.Stub { public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) { // reader synchronized (mPackages) { - final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet() - .iterator(); + final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProvidersByAuthority + .entrySet().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { Map.Entry<String, PackageParser.Provider> entry = i.next(); @@ -3341,7 +3377,7 @@ public class PackageManagerService extends IPackageManager.Stub { ArrayList<ProviderInfo> finalList = null; // reader synchronized (mPackages) { - final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); + final Iterator<PackageParser.Provider> i = mProviders.mProviders.values().iterator(); final int userId = processName != null ? UserHandle.getUserId(uid) : UserHandle.getCallingUserId(); while (i.hasNext()) { @@ -4313,8 +4349,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (p.info.authority != null) { String names[] = p.info.authority.split(";"); for (int j = 0; j < names.length; j++) { - if (mProviders.containsKey(names[j])) { - PackageParser.Provider other = mProviders.get(names[j]); + if (mProvidersByAuthority.containsKey(names[j])) { + PackageParser.Provider other = mProvidersByAuthority.get(names[j]); Slog.w(TAG, "Can't install because provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + ") is already used by " @@ -4745,8 +4781,7 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Provider p = pkg.providers.get(i); p.info.processName = fixProcessName(pkg.applicationInfo.processName, p.info.processName, pkg.applicationInfo.uid); - mProvidersByComponent.put(new ComponentName(p.info.packageName, - p.info.name), p); + mProviders.addProvider(p); p.syncable = p.info.isSyncable; if (p.info.authority != null) { String names[] = p.info.authority.split(";"); @@ -4763,8 +4798,8 @@ public class PackageManagerService extends IPackageManager.Stub { p = new PackageParser.Provider(p); p.syncable = false; } - if (!mProviders.containsKey(names[j])) { - mProviders.put(names[j], p); + if (!mProvidersByAuthority.containsKey(names[j])) { + mProvidersByAuthority.put(names[j], p); if (p.info.authority == null) { p.info.authority = names[j]; } else { @@ -4777,7 +4812,7 @@ public class PackageManagerService extends IPackageManager.Stub { + p.info.isSyncable); } } else { - PackageParser.Provider other = mProviders.get(names[j]); + PackageParser.Provider other = mProvidersByAuthority.get(names[j]); Slog.w(TAG, "Skipping provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + "): name already used by " @@ -5108,8 +5143,7 @@ public class PackageManagerService extends IPackageManager.Stub { int i; for (i=0; i<N; i++) { PackageParser.Provider p = pkg.providers.get(i); - mProvidersByComponent.remove(new ComponentName(p.info.packageName, - p.info.name)); + mProviders.removeProvider(p); if (p.info.authority == null) { /* There was another ContentProvider with this authority when @@ -5120,8 +5154,8 @@ public class PackageManagerService extends IPackageManager.Stub { } String names[] = p.info.authority.split(";"); for (int j = 0; j < names.length; j++) { - if (mProviders.get(names[j]) == p) { - mProviders.remove(names[j]); + if (mProvidersByAuthority.get(names[j]) == p) { + mProvidersByAuthority.remove(names[j]); if (DEBUG_REMOVE) { if (chatty) Log.d(TAG, "Unregistered content provider: " + names[j] @@ -5962,6 +5996,195 @@ public class PackageManagerService extends IPackageManager.Stub { private int mFlags; }; + private final class ProviderIntentResolver + extends IntentResolver<PackageParser.ProviderIntentInfo, ResolveInfo> { + 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); + } + + public 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); + } + + public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, + int flags, ArrayList<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 N = packageProviders.size(); + ArrayList<PackageParser.ProviderIntentInfo[]> listCut = + new ArrayList<PackageParser.ProviderIntentInfo[]>(N); + + ArrayList<PackageParser.ProviderIntentInfo> intentFilters; + for (int i = 0; i < N; ++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); + } + + public final void addProvider(PackageParser.Provider p) { + 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 NI = p.intents.size(); + int j; + for (j = 0; j < NI; 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); + } + } + + public final 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 NI = p.intents.size(); + int j; + for (j = 0; j < NI; 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 (!mSettings.isEnabledLPr(info.provider.info, mFlags, userId)) { + return null; + } + final PackageParser.Provider provider = info.provider; + if (mSafeMode && (provider.info.applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) == 0) { + return null; + } + PackageSetting ps = (PackageSetting) provider.owner.mExtras; + if (ps == null) { + return null; + } + ProviderInfo pi = PackageParser.generateProviderInfo(provider, mFlags, + ps.readUserState(userId), 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 = isSystemApp(res.providerInfo.applicationInfo); + return res; + } + + @Override + protected void sortResults(List<ResolveInfo> results) { + Collections.sort(results, mResolvePrioritySorter); + } + + @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))); + } + + private final HashMap<ComponentName, PackageParser.Provider> mProviders + = new HashMap<ComponentName, PackageParser.Provider>(); + private int mFlags; + }; + private static final Comparator<ResolveInfo> mResolvePrioritySorter = new Comparator<ResolveInfo>() { public int compare(ResolveInfo r1, ResolveInfo r2) { @@ -10454,6 +10677,11 @@ public class PackageManagerService extends IPackageManager.Stub { dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { dumpState.setTitlePrinted(true); } + if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:" + : "Provider Resolver Table:", " ", packageName, + dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { + dumpState.setTitlePrinted(true); + } } if (dumpState.isDumping(DumpState.DUMP_PREFERRED)) { @@ -10498,7 +10726,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { boolean printedSomething = false; - for (PackageParser.Provider p : mProvidersByComponent.values()) { + for (PackageParser.Provider p : mProviders.mProviders.values()) { if (packageName != null && !packageName.equals(p.info.packageName)) { continue; } @@ -10512,7 +10740,8 @@ public class PackageManagerService extends IPackageManager.Stub { pw.print(" "); pw.println(p.toString()); } printedSomething = false; - for (Map.Entry<String, PackageParser.Provider> entry : mProviders.entrySet()) { + for (Map.Entry<String, PackageParser.Provider> entry : + mProvidersByAuthority.entrySet()) { PackageParser.Provider p = entry.getValue(); if (packageName != null && !packageName.equals(p.info.packageName)) { continue; diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 5f944f6..661bd41 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -282,6 +282,18 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public List<ResolveInfo> queryIntentContentProvidersAsUser( + Intent intent, int flags, int userId) { + throw new UnsupportedOperationException(); + } + + @Override + public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) { + throw new UnsupportedOperationException(); + } + @Override public ProviderInfo resolveContentProvider(String name, int flags) { throw new UnsupportedOperationException(); |