Refactor directory API to "opening document tree."

Cleans up API so it consistently refers to opening or working with a
subtree of documents.  Also separates isChildDocument() support from
the concept of directory tree selection.

Bug: 15429194
Change-Id: Ice66a751ff4bd0cc4d34c44c5da13a0dc4186dc9
diff --git a/api/current.txt b/api/current.txt
index 791d037..53f361c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7388,6 +7388,7 @@
     field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
     field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
     field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
+    field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
     field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
     field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
     field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
@@ -7402,7 +7403,6 @@
     field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
     field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
     field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
-    field public static final java.lang.String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY";
     field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
     field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
     field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
@@ -23811,21 +23811,21 @@
 
   public final class DocumentsContract {
     method public static android.net.Uri buildChildDocumentsUri(java.lang.String, java.lang.String);
-    method public static android.net.Uri buildChildDocumentsViaUri(android.net.Uri, java.lang.String);
+    method public static android.net.Uri buildChildDocumentsUriUsingTree(android.net.Uri, java.lang.String);
     method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String);
-    method public static android.net.Uri buildDocumentViaUri(android.net.Uri, java.lang.String);
+    method public static android.net.Uri buildDocumentUriUsingTree(android.net.Uri, java.lang.String);
     method public static android.net.Uri buildRecentDocumentsUri(java.lang.String, java.lang.String);
     method public static android.net.Uri buildRootUri(java.lang.String, java.lang.String);
     method public static android.net.Uri buildRootsUri(java.lang.String);
     method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
-    method public static android.net.Uri buildViaUri(java.lang.String, java.lang.String);
+    method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
     method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
     method public static java.lang.String getRootId(android.net.Uri);
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
-    method public static java.lang.String getViaDocumentId(android.net.Uri);
+    method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
@@ -23864,7 +23864,7 @@
     field public static final java.lang.String COLUMN_TITLE = "title";
     field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
     field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
-    field public static final int FLAG_SUPPORTS_DIR_SELECTION = 16; // 0x10
+    field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10
     field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
     field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
   }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fad3851..cd0de12 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2731,6 +2731,7 @@
      * returned in {@link #getClipData()}.
      *
      * @see DocumentsContract
+     * @see #ACTION_OPEN_DOCUMENT_TREE
      * @see #ACTION_CREATE_DOCUMENT
      * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
      */
@@ -2765,28 +2766,30 @@
      *
      * @see DocumentsContract
      * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_OPEN_DOCUMENT_TREE
      * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
 
     /**
-     * Activity Action: Allow the user to pick a directory. When invoked, the
-     * system will display the various {@link DocumentsProvider} instances
-     * installed on the device, letting the user navigate through them. Apps can
-     * fully manage documents within the returned directory.
+     * Activity Action: Allow the user to pick a directory subtree. When
+     * invoked, the system will display the various {@link DocumentsProvider}
+     * instances installed on the device, letting the user navigate through
+     * them. Apps can fully manage documents within the returned directory.
      * <p>
      * To gain access to descendant (child, grandchild, etc) documents, use
-     * {@link DocumentsContract#buildDocumentViaUri(Uri, String)} and
-     * {@link DocumentsContract#buildChildDocumentsViaUri(Uri, String)} using
-     * the returned directory URI.
+     * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)} and
+     * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)}
+     * with the returned URI.
      * <p>
-     * Output: The URI representing the selected directory.
+     * Output: The URI representing the selected directory tree.
      *
      * @see DocumentsContract
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY";
+    public static final String
+            ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
 
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
@@ -3365,8 +3368,8 @@
      *
      * @see #ACTION_GET_CONTENT
      * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_OPEN_DOCUMENT_TREE
      * @see #ACTION_CREATE_DOCUMENT
-     * @see #ACTION_PICK_DIRECTORY
      */
     public static final String EXTRA_LOCAL_ONLY =
             "android.intent.extra.LOCAL_ONLY";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 6b8e2de..327fe4a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -60,7 +60,8 @@
  * <p>
  * All client apps must hold a valid URI permission grant to access documents,
  * typically issued when a user makes a selection through
- * {@link Intent#ACTION_OPEN_DOCUMENT} or {@link Intent#ACTION_CREATE_DOCUMENT}.
+ * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT},
+ * or {@link Intent#ACTION_OPEN_DOCUMENT_TREE}.
  *
  * @see DocumentsProvider
  */
@@ -73,8 +74,8 @@
     // content://com.example/root/sdcard/search/?query=pony
     // content://com.example/document/12/
     // content://com.example/document/12/children/
-    // content://com.example/via/12/document/24/
-    // content://com.example/via/12/document/24/children/
+    // content://com.example/tree/12/document/24/
+    // content://com.example/tree/12/document/24/children/
 
     private DocumentsContract() {
     }
@@ -441,12 +442,13 @@
         public static final int FLAG_SUPPORTS_SEARCH = 1 << 3;
 
         /**
-         * Flag indicating that this root supports directory selection.
+         * Flag indicating that this root supports testing parent child
+         * relationships.
          *
          * @see #COLUMN_FLAGS
          * @see DocumentsProvider#isChildDocument(String, String)
          */
-        public static final int FLAG_SUPPORTS_DIR_SELECTION = 1 << 4;
+        public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4;
 
         /**
          * Flag indicating that this root is currently empty. This may be used
@@ -518,7 +520,7 @@
     private static final String PATH_DOCUMENT = "document";
     private static final String PATH_CHILDREN = "children";
     private static final String PATH_SEARCH = "search";
-    private static final String PATH_VIA = "via";
+    private static final String PATH_TREE = "tree";
 
     private static final String PARAM_QUERY = "query";
     private static final String PARAM_MANAGE = "manage";
@@ -564,17 +566,17 @@
      * Build URI representing access to descendant documents of the given
      * {@link Document#COLUMN_DOCUMENT_ID}.
      *
-     * @see #getViaDocumentId(Uri)
+     * @see #getTreeDocumentId(Uri)
      */
-    public static Uri buildViaUri(String authority, String documentId) {
+    public static Uri buildTreeDocumentUri(String authority, String documentId) {
         return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
-                .appendPath(PATH_VIA).appendPath(documentId).build();
+                .appendPath(PATH_TREE).appendPath(documentId).build();
     }
 
     /**
-     * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
-     * document provider. When queried, a provider will return a single row with
-     * columns defined by {@link Document}.
+     * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in
+     * a document provider. When queried, a provider will return a single row
+     * with columns defined by {@link Document}.
      *
      * @see DocumentsProvider#queryDocument(String, String[])
      * @see #getDocumentId(Uri)
@@ -585,42 +587,46 @@
     }
 
     /**
-     * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
-     * document provider. Instead of directly accessing the target document,
-     * gain access via another document. The target document must be a
-     * descendant (child, grandchild, etc) of the via document.
+     * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in
+     * a document provider. When queried, a provider will return a single row
+     * with columns defined by {@link Document}.
+     * <p>
+     * However, instead of directly accessing the target document, the returned
+     * URI will leverage access granted through a subtree URI, typically
+     * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target document
+     * must be a descendant (child, grandchild, etc) of the subtree.
      * <p>
      * This is typically used to access documents under a user-selected
-     * directory, since it doesn't require the user to separately confirm each
-     * new document access.
+     * directory tree, since it doesn't require the user to separately confirm
+     * each new document access.
      *
-     * @param viaUri a related document (directory) that the caller is
-     *            leveraging to gain access to the target document. The target
-     *            document must be a descendant of this directory.
+     * @param treeUri the subtree to leverage to gain access to the target
+     *            document. The target directory must be a descendant of this
+     *            subtree.
      * @param documentId the target document, which the caller may not have
      *            direct access to.
-     * @see Intent#ACTION_PICK_DIRECTORY
+     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
      * @see DocumentsProvider#isChildDocument(String, String)
      * @see #buildDocumentUri(String, String)
      */
-    public static Uri buildDocumentViaUri(Uri viaUri, String documentId) {
+    public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) {
         return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(viaUri.getAuthority()).appendPath(PATH_VIA)
-                .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT)
+                .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
+                .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
                 .appendPath(documentId).build();
     }
 
     /** {@hide} */
-    public static Uri buildDocumentMaybeViaUri(Uri baseUri, String documentId) {
-        if (isViaUri(baseUri)) {
-            return buildDocumentViaUri(baseUri, documentId);
+    public static Uri buildDocumentUriMaybeUsingTree(Uri baseUri, String documentId) {
+        if (isTreeUri(baseUri)) {
+            return buildDocumentUriUsingTree(baseUri, documentId);
         } else {
             return buildDocumentUri(baseUri.getAuthority(), documentId);
         }
     }
 
     /**
-     * Build URI representing the children of the given directory in a document
+     * Build URI representing the children of the target directory in a document
      * provider. When queried, a provider will return zero or more rows with
      * columns defined by {@link Document}.
      *
@@ -637,28 +643,33 @@
     }
 
     /**
-     * Build URI representing the children of the given directory in a document
-     * provider. Instead of directly accessing the target document, gain access
-     * via another document. The target document must be a descendant (child,
-     * grandchild, etc) of the via document.
+     * Build URI representing the children of the target directory in a document
+     * provider. When queried, a provider will return zero or more rows with
+     * columns defined by {@link Document}.
+     * <p>
+     * However, instead of directly accessing the target directory, the returned
+     * URI will leverage access granted through a subtree URI, typically
+     * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target
+     * directory must be a descendant (child, grandchild, etc) of the subtree.
      * <p>
      * This is typically used to access documents under a user-selected
-     * directory, since it doesn't require the user to separately confirm each
-     * new document access.
+     * directory tree, since it doesn't require the user to separately confirm
+     * each new document access.
      *
-     * @param viaUri a related document (directory) that the caller is
-     *            leveraging to gain access to the target document. The target
-     *            document must be a descendant of this directory.
-     * @param parentDocumentId the target document, which the caller may not
-     *            have direct access to.
-     * @see Intent#ACTION_PICK_DIRECTORY
+     * @param treeUri the subtree to leverage to gain access to the target
+     *            document. The target directory must be a descendant of this
+     *            subtree.
+     * @param parentDocumentId the document to return children for, which the
+     *            caller may not have direct access to, and which must be a
+     *            directory with MIME type of {@link Document#MIME_TYPE_DIR}.
+     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
      * @see DocumentsProvider#isChildDocument(String, String)
      * @see #buildChildDocumentsUri(String, String)
      */
-    public static Uri buildChildDocumentsViaUri(Uri viaUri, String parentDocumentId) {
+    public static Uri buildChildDocumentsUriUsingTree(Uri treeUri, String parentDocumentId) {
         return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(viaUri.getAuthority()).appendPath(PATH_VIA)
-                .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT)
+                .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
+                .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
                 .appendPath(parentDocumentId).appendPath(PATH_CHILDREN).build();
     }
 
@@ -683,21 +694,24 @@
      * {@link DocumentsProvider}.
      *
      * @see #buildDocumentUri(String, String)
-     * @see #buildDocumentViaUri(Uri, String)
+     * @see #buildDocumentUriUsingTree(Uri, String)
      */
     public static boolean isDocumentUri(Context context, Uri uri) {
         final List<String> paths = uri.getPathSegments();
-        if (paths.size() >= 2
-                && (PATH_DOCUMENT.equals(paths.get(0)) || PATH_VIA.equals(paths.get(0)))) {
+        if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) {
+            return isDocumentsProvider(context, uri.getAuthority());
+        }
+        if (paths.size() == 4 && PATH_TREE.equals(paths.get(0))
+                && PATH_DOCUMENT.equals(paths.get(2))) {
             return isDocumentsProvider(context, uri.getAuthority());
         }
         return false;
     }
 
     /** {@hide} */
-    public static boolean isViaUri(Uri uri) {
+    public static boolean isTreeUri(Uri uri) {
         final List<String> paths = uri.getPathSegments();
-        return (paths.size() >= 2 && PATH_VIA.equals(paths.get(0)));
+        return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
     }
 
     private static boolean isDocumentsProvider(Context context, String authority) {
@@ -733,7 +747,7 @@
         if (paths.size() >= 2 && PATH_DOCUMENT.equals(paths.get(0))) {
             return paths.get(1);
         }
-        if (paths.size() >= 4 && PATH_VIA.equals(paths.get(0))
+        if (paths.size() >= 4 && PATH_TREE.equals(paths.get(0))
                 && PATH_DOCUMENT.equals(paths.get(2))) {
             return paths.get(3);
         }
@@ -742,12 +756,10 @@
 
     /**
      * Extract the via {@link Document#COLUMN_DOCUMENT_ID} from the given URI.
-     *
-     * @see #isViaUri(Uri)
      */
-    public static String getViaDocumentId(Uri documentUri) {
+    public static String getTreeDocumentId(Uri documentUri) {
         final List<String> paths = documentUri.getPathSegments();
-        if (paths.size() >= 2 && PATH_VIA.equals(paths.get(0))) {
+        if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) {
             return paths.get(1);
         }
         throw new IllegalArgumentException("Invalid URI: " + documentUri);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 066b4aa..021fff4 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -20,10 +20,14 @@
 import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
+import static android.provider.DocumentsContract.buildDocumentUri;
+import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
+import static android.provider.DocumentsContract.buildTreeDocumentUri;
 import static android.provider.DocumentsContract.getDocumentId;
 import static android.provider.DocumentsContract.getRootId;
 import static android.provider.DocumentsContract.getSearchDocumentsQuery;
-import static android.provider.DocumentsContract.isViaUri;
+import static android.provider.DocumentsContract.getTreeDocumentId;
+import static android.provider.DocumentsContract.isTreeUri;
 
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -117,6 +121,7 @@
  * </p>
  *
  * @see Intent#ACTION_OPEN_DOCUMENT
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
  * @see Intent#ACTION_CREATE_DOCUMENT
  */
 public abstract class DocumentsProvider extends ContentProvider {
@@ -128,8 +133,8 @@
     private static final int MATCH_SEARCH = 4;
     private static final int MATCH_DOCUMENT = 5;
     private static final int MATCH_CHILDREN = 6;
-    private static final int MATCH_DOCUMENT_VIA = 7;
-    private static final int MATCH_CHILDREN_VIA = 8;
+    private static final int MATCH_DOCUMENT_TREE = 7;
+    private static final int MATCH_CHILDREN_TREE = 8;
 
     private String mAuthority;
 
@@ -149,8 +154,8 @@
         mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
         mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
         mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
-        mMatcher.addURI(mAuthority, "via/*/document/*", MATCH_DOCUMENT_VIA);
-        mMatcher.addURI(mAuthority, "via/*/document/*/children", MATCH_CHILDREN_VIA);
+        mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE);
+        mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE);
 
         // Sanity check our setup
         if (!info.exported) {
@@ -169,23 +174,24 @@
 
     /**
      * Test if a document is descendant (child, grandchild, etc) from the given
-     * parent. Providers must override this to support directory selection. You
-     * should avoid making network requests to keep this request fast.
+     * parent. For example, providers must implement this to support
+     * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. You should avoid making network
+     * requests to keep this request fast.
      *
      * @param parentDocumentId parent to verify against.
      * @param documentId child to verify.
      * @return if given document is a descendant of the given parent.
-     * @see DocumentsContract.Root#FLAG_SUPPORTS_DIR_SELECTION
+     * @see DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD
      */
     public boolean isChildDocument(String parentDocumentId, String documentId) {
         return false;
     }
 
     /** {@hide} */
-    private void enforceVia(Uri documentUri) {
-        if (DocumentsContract.isViaUri(documentUri)) {
-            final String parent = DocumentsContract.getViaDocumentId(documentUri);
-            final String child = DocumentsContract.getDocumentId(documentUri);
+    private void enforceTree(Uri documentUri) {
+        if (isTreeUri(documentUri)) {
+            final String parent = getTreeDocumentId(documentUri);
+            final String child = getDocumentId(documentUri);
             if (Objects.equals(parent, child)) {
                 return;
             }
@@ -479,12 +485,12 @@
                     return querySearchDocuments(
                             getRootId(uri), getSearchDocumentsQuery(uri), projection);
                 case MATCH_DOCUMENT:
-                case MATCH_DOCUMENT_VIA:
-                    enforceVia(uri);
+                case MATCH_DOCUMENT_TREE:
+                    enforceTree(uri);
                     return queryDocument(getDocumentId(uri), projection);
                 case MATCH_CHILDREN:
-                case MATCH_CHILDREN_VIA:
-                    enforceVia(uri);
+                case MATCH_CHILDREN_TREE:
+                    enforceTree(uri);
                     if (DocumentsContract.isManageMode(uri)) {
                         return queryChildDocumentsForManage(
                                 getDocumentId(uri), projection, sortOrder);
@@ -512,8 +518,8 @@
                 case MATCH_ROOT:
                     return DocumentsContract.Root.MIME_TYPE_ITEM;
                 case MATCH_DOCUMENT:
-                case MATCH_DOCUMENT_VIA:
-                    enforceVia(uri);
+                case MATCH_DOCUMENT_TREE:
+                    enforceTree(uri);
                     return getDocumentType(getDocumentId(uri));
                 default:
                     return null;
@@ -530,21 +536,20 @@
      * call the superclass. If the superclass returns {@code null}, the subclass
      * may implement custom behavior.
      * <p>
-     * This is typically used to resolve a "via" URI into a concrete document
+     * This is typically used to resolve a subtree URI into a concrete document
      * reference, issuing a narrower single-document URI permission grant along
      * the way.
      *
-     * @see DocumentsContract#buildDocumentViaUri(Uri, String)
+     * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String)
      */
     @Override
     public Uri canonicalize(Uri uri) {
         final Context context = getContext();
         switch (mMatcher.match(uri)) {
-            case MATCH_DOCUMENT_VIA:
-                enforceVia(uri);
+            case MATCH_DOCUMENT_TREE:
+                enforceTree(uri);
 
-                final Uri narrowUri = DocumentsContract.buildDocumentUri(uri.getAuthority(),
-                        DocumentsContract.getDocumentId(uri));
+                final Uri narrowUri = buildDocumentUri(uri.getAuthority(), getDocumentId(uri));
 
                 // Caller may only have prefix grant, so extend them a grant to
                 // the narrow URI.
@@ -628,7 +633,7 @@
             throw new SecurityException(
                     "Requested authority " + authority + " doesn't match provider " + mAuthority);
         }
-        enforceVia(documentUri);
+        enforceTree(documentUri);
 
         final Bundle out = new Bundle();
         try {
@@ -641,8 +646,8 @@
 
                 // No need to issue new grants here, since caller either has
                 // manage permission or a prefix grant. We might generate a
-                // "via" style URI if that's how they called us.
-                final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(documentUri,
+                // tree style URI if that's how they called us.
+                final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
                         newDocumentId);
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
@@ -653,12 +658,12 @@
                 final String newDocumentId = renameDocument(documentId, displayName);
 
                 if (newDocumentId != null) {
-                    final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(
-                            documentUri, newDocumentId);
+                    final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
+                            newDocumentId);
 
                     // If caller came in with a narrow grant, issue them a
                     // narrow grant for the newly renamed document.
-                    if (!isViaUri(newDocumentUri)) {
+                    if (!isTreeUri(newDocumentUri)) {
                         final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
                                 documentUri);
                         context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
@@ -694,8 +699,8 @@
      */
     public final void revokeDocumentPermission(String documentId) {
         final Context context = getContext();
-        context.revokeUriPermission(DocumentsContract.buildDocumentUri(mAuthority, documentId), ~0);
-        context.revokeUriPermission(DocumentsContract.buildViaUri(mAuthority, documentId), ~0);
+        context.revokeUriPermission(buildDocumentUri(mAuthority, documentId), ~0);
+        context.revokeUriPermission(buildTreeDocumentUri(mAuthority, documentId), ~0);
     }
 
     /**
@@ -705,7 +710,7 @@
      */
     @Override
     public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
-        enforceVia(uri);
+        enforceTree(uri);
         return openDocument(getDocumentId(uri), mode, null);
     }
 
@@ -717,7 +722,7 @@
     @Override
     public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
             throws FileNotFoundException {
-        enforceVia(uri);
+        enforceTree(uri);
         return openDocument(getDocumentId(uri), mode, signal);
     }
 
@@ -730,7 +735,7 @@
     @SuppressWarnings("resource")
     public final AssetFileDescriptor openAssetFile(Uri uri, String mode)
             throws FileNotFoundException {
-        enforceVia(uri);
+        enforceTree(uri);
         final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, null);
         return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
     }
@@ -744,7 +749,7 @@
     @SuppressWarnings("resource")
     public final AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal)
             throws FileNotFoundException {
-        enforceVia(uri);
+        enforceTree(uri);
         final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, signal);
         return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
     }
@@ -757,7 +762,7 @@
     @Override
     public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
             throws FileNotFoundException {
-        enforceVia(uri);
+        enforceTree(uri);
         if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
             final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
             return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
@@ -775,7 +780,7 @@
     public final AssetFileDescriptor openTypedAssetFile(
             Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
             throws FileNotFoundException {
-        enforceVia(uri);
+        enforceTree(uri);
         if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
             final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
             return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 159ee66..3861cc1 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -32,7 +32,7 @@
                 <data android:mimeType="*/*" />
             </intent-filter>
             <intent-filter>
-                <action android:name="android.intent.action.PICK_DIRECTORY" />
+                <action android:name="android.intent.action.OPEN_DOCUMENT_TREE" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <intent-filter>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 9f76991..d0b6a1d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -24,7 +24,7 @@
 import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
 import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
 import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_PICK_DIRECTORY;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN_TREE;
 import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
 import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
 
@@ -203,7 +203,7 @@
             final String mimeType = getIntent().getType();
             final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
             SaveFragment.show(getFragmentManager(), mimeType, title);
-        } else if (mState.action == ACTION_PICK_DIRECTORY) {
+        } else if (mState.action == ACTION_OPEN_TREE) {
             PickFragment.show(getFragmentManager());
         }
 
@@ -213,7 +213,7 @@
             moreApps.setPackage(null);
             RootsFragment.show(getFragmentManager(), moreApps);
         } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE
-                || mState.action == ACTION_PICK_DIRECTORY) {
+                || mState.action == ACTION_OPEN_TREE) {
             RootsFragment.show(getFragmentManager(), null);
         }
 
@@ -240,8 +240,8 @@
             mState.action = ACTION_CREATE;
         } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
             mState.action = ACTION_GET_CONTENT;
-        } else if (Intent.ACTION_PICK_DIRECTORY.equals(action)) {
-            mState.action = ACTION_PICK_DIRECTORY;
+        } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
+            mState.action = ACTION_OPEN_TREE;
         } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
             mState.action = ACTION_MANAGE;
         }
@@ -441,7 +441,7 @@
             actionBar.setIcon(new ColorDrawable());
 
             if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT
-                    || mState.action == ACTION_PICK_DIRECTORY) {
+                    || mState.action == ACTION_OPEN_TREE) {
                 actionBar.setTitle(R.string.title_open);
             } else if (mState.action == ACTION_CREATE) {
                 actionBar.setTitle(R.string.title_save);
@@ -583,7 +583,7 @@
         sortSize.setVisible(mState.showSize);
 
         final boolean searchVisible;
-        if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) {
+        if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
             createDir.setVisible(cwd != null && cwd.isCreateSupported());
             searchVisible = false;
 
@@ -828,7 +828,7 @@
 
         if (cwd == null) {
             // No directory means recents
-            if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) {
+            if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
                 RecentsCreateFragment.show(fm);
             } else {
                 DirectoryFragment.showRecentsOpen(fm, anim);
@@ -857,7 +857,7 @@
             }
         }
 
-        if (mState.action == ACTION_PICK_DIRECTORY) {
+        if (mState.action == ACTION_OPEN_TREE) {
             final PickFragment pick = PickFragment.get(fm);
             if (pick != null) {
                 final CharSequence displayName = (mState.stack.size() <= 1) ? root.title
@@ -1021,7 +1021,7 @@
     }
 
     public void onPickRequested(DocumentInfo pickTarget) {
-        final Uri viaUri = DocumentsContract.buildViaUri(pickTarget.authority,
+        final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
                 pickTarget.documentId);
         new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
     }
@@ -1031,7 +1031,7 @@
         final ContentValues values = new ContentValues();
 
         final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
-        if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) {
+        if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
             // Remember stack for last create
             values.clear();
             values.put(RecentColumns.KEY, mState.stack.buildKey());
@@ -1064,7 +1064,7 @@
 
         if (mState.action == ACTION_GET_CONTENT) {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        } else if (mState.action == ACTION_PICK_DIRECTORY) {
+        } else if (mState.action == ACTION_OPEN_TREE) {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
@@ -1202,7 +1202,7 @@
         public static final int ACTION_OPEN = 1;
         public static final int ACTION_CREATE = 2;
         public static final int ACTION_GET_CONTENT = 3;
-        public static final int ACTION_PICK_DIRECTORY = 4;
+        public static final int ACTION_OPEN_TREE = 4;
         public static final int ACTION_MANAGE = 5;
 
         public static final int MODE_UNKNOWN = 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index a9e488a1..5112c92 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -77,13 +77,15 @@
     public void setPickTarget(DocumentInfo pickTarget, CharSequence displayName) {
         mPickTarget = pickTarget;
 
-        if (mPickTarget != null) {
-            mContainer.setVisibility(View.VISIBLE);
-            final Locale locale = getResources().getConfiguration().locale;
-            final String raw = getString(R.string.menu_select).toUpperCase(locale);
-            mPick.setText(TextUtils.expandTemplate(raw, displayName));
-        } else {
-            mContainer.setVisibility(View.GONE);
+        if (mContainer != null) {
+            if (mPickTarget != null) {
+                mContainer.setVisibility(View.VISIBLE);
+                final Locale locale = getResources().getConfiguration().locale;
+                final String raw = getString(R.string.menu_select).toUpperCase(locale);
+                mPick.setText(TextUtils.expandTemplate(raw, displayName));
+            } else {
+                mContainer.setVisibility(View.GONE);
+            }
         }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 933dbe0..caa7581 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -105,7 +105,7 @@
         mRecentsRoot.rootId = null;
         mRecentsRoot.icon = R.drawable.ic_root_recent;
         mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE
-                | Root.FLAG_SUPPORTS_DIR_SELECTION;
+                | Root.FLAG_SUPPORTS_IS_CHILD;
         mRecentsRoot.title = mContext.getString(R.string.root_recent);
         mRecentsRoot.availableBytes = -1;
 
@@ -350,7 +350,7 @@
         final List<RootInfo> matching = Lists.newArrayList();
         for (RootInfo root : roots) {
             final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0;
-            final boolean supportsDir = (root.flags & Root.FLAG_SUPPORTS_DIR_SELECTION) != 0;
+            final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
             final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0;
             final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0;
             final boolean empty = (root.flags & Root.FLAG_EMPTY) != 0;
@@ -358,7 +358,7 @@
             // Exclude read-only devices when creating
             if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
             // Exclude roots that don't support directory picking
-            if (state.action == State.ACTION_PICK_DIRECTORY && !supportsDir) continue;
+            if (state.action == State.ACTION_OPEN_TREE && !supportsIsChild) continue;
             // Exclude advanced devices when not requested
             if (!state.showAdvanced && advanced) continue;
             // Exclude non-local devices when local only
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index d388ab7..9473eb9 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -145,7 +145,7 @@
                 final RootInfo root = new RootInfo();
                 root.rootId = rootId;
                 root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
-                        | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_DIR_SELECTION;
+                        | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
                 if (ROOT_ID_PRIMARY_EMULATED.equals(rootId)) {
                     root.title = getContext().getString(R.string.root_internal_storage);
                 } else {