Add <intent-filter> support to <provider>.

For the new documents work, we're only interested in the subset of
ContentProviders that actually implement DocumentsContract.  Instead
of returning all providers, add <intent-filter> support to make it
easier to limit the set of returned ProviderInfo.

Define a well-known action for DocumentsProviders, and start using it
when querying for roots.  Continue supporting the old <meta-data>
approach until all apps have been updated.

Bug: 8599233
Change-Id: I05f049bba21311f5421738002f99ee214447c909
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 9203af9..ba9c9f5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2202,6 +2202,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 b489ee9e..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) {