am 9d1383c6: am 5e02e0a9: am bcc77b50: Merge "Add <intent-filter> support to <provider>." into klp-dev

* commit '9d1383c61ca0932cb36b104a89c32db15de6a10e':
  Add <intent-filter> support to <provider>.
diff --git a/api/current.txt b/api/current.txt
index 389843c..4eba3a5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7290,6 +7290,7 @@
     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);
@@ -7455,6 +7456,7 @@
     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;
@@ -7488,6 +7490,7 @@
     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;
@@ -21045,6 +21048,7 @@
     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 {
@@ -24540,6 +24544,7 @@
     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 @@
     }
 
     @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 @@
     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 c0a7905..6642c30 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2218,6 +2218,24 @@
     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&lt;ResolveInfo&gt; 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 @@
                 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 @@
         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 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 @@
 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 @@
         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.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 @@
  * &lt;intent&gt; 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 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 @@
                 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 @@
                 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 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 @@
         } 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 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 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 @@
         } 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 @@
     };
 
     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 @@
     }
 
     /** {@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 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 @@
     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 @@
         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 @@
     // 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 @@
         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 @@
     }
 
     @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 @@
         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 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 @@
         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 @@
                     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 @@
                 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 @@
                             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 @@
                                             + 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 @@
         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 @@
             }
             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 @@
         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 @@
                         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 @@
 
             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 @@
                     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 @@
         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();