Refactoring of DocumentsContract.

Combines related columns and constants onto the same class so they
are easier to discover.  Move back to surfacing roots with columns
so they are consistent with documents.

Advanced roots are represented with a flag instead of distinct
types.  Flags to indicate supporting of well-known media types,
instead of arbitrary an MIME filter.  Reintroduce well-formed rootId
to support recents.

Always use the expanded version of "documents" in constants, methods,
and argument names.

Refactor DocumentProvider method names to clearly distinguish if
a single item or multiple could be returned, and of which type.  Add
documentation to clearly define which methods have already been
overridden.

Bug: 10567506, 10567557
Change-Id: I981f26ab82f2b520a19aa1ce66f659de50d7fac0
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 6bc554f..e0b8d19 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -20,7 +20,6 @@
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.FragmentManager;
-import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -28,13 +27,13 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Documents;
+import android.provider.DocumentsContract.Document;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.EditText;
 import android.widget.Toast;
 
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
 
 /**
  * Dialog to create a new directory.
@@ -67,24 +66,17 @@
                 final String displayName = text1.getText().toString();
 
                 final DocumentsActivity activity = (DocumentsActivity) getActivity();
-                final Document cwd = activity.getCurrentDirectory();
+                final DocumentInfo cwd = activity.getCurrentDirectory();
 
-                final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                        cwd.uri.getAuthority());
                 try {
-                    final String docId = DocumentsContract.createDocument(client,
-                            DocumentsContract.getDocId(cwd.uri), Documents.MIME_TYPE_DIR,
-                            displayName);
+                    final Uri childUri = DocumentsContract.createDocument(
+                            resolver, cwd.uri, Document.MIME_TYPE_DIR, displayName);
 
                     // Navigate into newly created child
-                    final Uri childUri = DocumentsContract.buildDocumentUri(
-                            cwd.uri.getAuthority(), docId);
-                    final Document childDoc = Document.fromUri(resolver, childUri);
+                    final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri);
                     activity.onDocumentPicked(childDoc);
                 } catch (Exception e) {
                     Toast.makeText(context, R.string.save_error, Toast.LENGTH_SHORT).show();
-                } finally {
-                    ContentProviderClient.closeQuietly(client);
                 }
             }
         });
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 783b6ff..79d20a4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -61,7 +61,7 @@
 import android.widget.Toast;
 
 import com.android.documentsui.DocumentsActivity.DisplayState;
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
 import com.android.internal.util.Predicate;
 import com.google.android.collect.Lists;
 
@@ -81,7 +81,7 @@
 
     private AbsListView mCurrentView;
 
-    private Predicate<Document> mFilter;
+    private Predicate<DocumentInfo> mFilter;
 
     public static final int TYPE_NORMAL = 1;
     public static final int TYPE_SEARCH = 2;
@@ -106,8 +106,8 @@
     }
 
     public static void showSearch(FragmentManager fm, Uri uri, String query) {
-        final Uri searchUri = DocumentsContract.buildSearchUri(
-                uri.getAuthority(), DocumentsContract.getDocId(uri), query);
+        final Uri searchUri = DocumentsContract.buildSearchDocumentsUri(
+                uri.getAuthority(), DocumentsContract.getDocumentId(uri), query);
         show(fm, TYPE_SEARCH, searchUri);
     }
 
@@ -163,21 +163,21 @@
 
                 Uri contentsUri;
                 if (mType == TYPE_NORMAL) {
-                    contentsUri = DocumentsContract.buildChildrenUri(
-                            uri.getAuthority(), DocumentsContract.getDocId(uri));
+                    contentsUri = DocumentsContract.buildChildDocumentsUri(
+                            uri.getAuthority(), DocumentsContract.getDocumentId(uri));
                 } else if (mType == TYPE_RECENT_OPEN) {
                     contentsUri = RecentsProvider.buildRecentOpen();
                 } else {
                     contentsUri = uri;
                 }
 
-                final Comparator<Document> sortOrder;
+                final Comparator<DocumentInfo> sortOrder;
                 if (state.sortOrder == SORT_ORDER_LAST_MODIFIED || mType == TYPE_RECENT_OPEN) {
-                    sortOrder = new Document.LastModifiedComparator();
+                    sortOrder = new DocumentInfo.LastModifiedComparator();
                 } else if (state.sortOrder == SORT_ORDER_DISPLAY_NAME) {
-                    sortOrder = new Document.DisplayNameComparator();
+                    sortOrder = new DocumentInfo.DisplayNameComparator();
                 } else if (state.sortOrder == SORT_ORDER_SIZE) {
-                    sortOrder = new Document.SizeComparator();
+                    sortOrder = new DocumentInfo.SizeComparator();
                 } else {
                     throw new IllegalArgumentException("Unknown sort order " + state.sortOrder);
                 }
@@ -258,7 +258,7 @@
     private OnItemClickListener mItemListener = new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            final Document doc = mAdapter.getItem(position);
+            final DocumentInfo doc = mAdapter.getItem(position);
             if (mFilter.apply(doc)) {
                 ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
             }
@@ -291,11 +291,11 @@
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
             final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
-            final ArrayList<Document> docs = Lists.newArrayList();
+            final ArrayList<DocumentInfo> docs = Lists.newArrayList();
             final int size = checked.size();
             for (int i = 0; i < size; i++) {
                 if (checked.valueAt(i)) {
-                    final Document doc = mAdapter.getItem(checked.keyAt(i));
+                    final DocumentInfo doc = mAdapter.getItem(checked.keyAt(i));
                     docs.add(doc);
                 }
             }
@@ -328,7 +328,7 @@
                 ActionMode mode, int position, long id, boolean checked) {
             if (checked) {
                 // Directories cannot be checked
-                final Document doc = mAdapter.getItem(position);
+                final DocumentInfo doc = mAdapter.getItem(position);
                 if (doc.isDirectory()) {
                     mCurrentView.setItemChecked(position, false);
                 }
@@ -339,9 +339,9 @@
         }
     };
 
-    private void onShareDocuments(List<Document> docs) {
+    private void onShareDocuments(List<DocumentInfo> docs) {
         final ArrayList<Uri> uris = Lists.newArrayList();
-        for (Document doc : docs) {
+        for (DocumentInfo doc : docs) {
             uris.add(doc.uri);
         }
 
@@ -363,12 +363,12 @@
         startActivity(intent);
     }
 
-    private void onDeleteDocuments(List<Document> docs) {
+    private void onDeleteDocuments(List<DocumentInfo> docs) {
         final Context context = getActivity();
         final ContentResolver resolver = context.getContentResolver();
 
         boolean hadTrouble = false;
-        for (Document doc : docs) {
+        for (DocumentInfo doc : docs) {
             if (!doc.isDeleteSupported()) {
                 Log.w(TAG, "Skipping " + doc);
                 hadTrouble = true;
@@ -396,12 +396,12 @@
     }
 
     private class DocumentsAdapter extends BaseAdapter {
-        private List<Document> mDocuments;
+        private List<DocumentInfo> mDocuments;
 
         public DocumentsAdapter() {
         }
 
-        public void swapDocuments(List<Document> documents) {
+        public void swapDocuments(List<DocumentInfo> documents) {
             mDocuments = documents;
 
             if (mDocuments != null && mDocuments.isEmpty()) {
@@ -433,7 +433,7 @@
                 }
             }
 
-            final Document doc = getItem(position);
+            final DocumentInfo doc = getItem(position);
 
             final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
@@ -507,7 +507,7 @@
         }
 
         @Override
-        public Document getItem(int position) {
+        public DocumentInfo getItem(int position) {
             return mDocuments.get(position);
         }
 
@@ -538,8 +538,8 @@
 
             Bitmap result = null;
             try {
-                result = DocumentsContract.getThumbnail(
-                        context.getContentResolver(), uri, mThumbSize);
+                result = DocumentsContract.getDocumentThumbnail(
+                        context.getContentResolver(), uri, mThumbSize, null);
                 if (result != null) {
                     final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
                             context, mThumbSize);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 4ce5ef8..cb92d76 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -28,7 +28,7 @@
 import android.os.CancellationSignal;
 import android.util.Log;
 
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
 import com.android.internal.util.Predicate;
 import com.google.android.collect.Lists;
 
@@ -41,7 +41,7 @@
 
 class DirectoryResult implements AutoCloseable {
     Cursor cursor;
-    List<Document> contents = Lists.newArrayList();
+    List<DocumentInfo> contents = Lists.newArrayList();
     Exception e;
 
     @Override
@@ -53,11 +53,11 @@
 public class DirectoryLoader extends UriDerivativeLoader<Uri, DirectoryResult> {
 
     private final int mType;
-    private Predicate<Document> mFilter;
-    private Comparator<Document> mSortOrder;
+    private Predicate<DocumentInfo> mFilter;
+    private Comparator<DocumentInfo> mSortOrder;
 
-    public DirectoryLoader(Context context, Uri uri, int type, Predicate<Document> filter,
-            Comparator<Document> sortOrder) {
+    public DirectoryLoader(Context context, Uri uri, int type, Predicate<DocumentInfo> filter,
+            Comparator<DocumentInfo> sortOrder) {
         super(context, uri);
         mType = type;
         mFilter = filter;
@@ -84,15 +84,15 @@
         result.cursor.registerContentObserver(mObserver);
 
         while (cursor.moveToNext()) {
-            Document doc = null;
+            DocumentInfo doc = null;
             switch (mType) {
                 case TYPE_NORMAL:
                 case TYPE_SEARCH:
-                    doc = Document.fromDirectoryCursor(uri, cursor);
+                    doc = DocumentInfo.fromDirectoryCursor(uri, cursor);
                     break;
                 case TYPE_RECENT_OPEN:
                     try {
-                        doc = Document.fromRecentOpenCursor(resolver, cursor);
+                        doc = DocumentInfo.fromRecentOpenCursor(resolver, cursor);
                     } catch (FileNotFoundException e) {
                         Log.w(TAG, "Failed to find recent: " + e);
                     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentChangedReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentChangedReceiver.java
index 0ce5968..54f62ef 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentChangedReceiver.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentChangedReceiver.java
@@ -21,11 +21,10 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.provider.DocumentsContract.DocumentRoot;
 import android.util.Log;
 
 /**
- * Handles {@link DocumentRoot} changes which invalidate cached data.
+ * Handles changes which invalidate cached data.
  */
 public class DocumentChangedReceiver extends BroadcastReceiver {
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 73ca8fa..912d6dc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -42,7 +42,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.DocumentRoot;
 import android.support.v4.app.ActionBarDrawerToggle;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
@@ -60,8 +59,9 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.RootInfo;
 
 import java.io.FileNotFoundException;
 import java.util.Arrays;
@@ -160,7 +160,7 @@
             mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
 
             final Uri rootUri = intent.getData();
-            final DocumentRoot root = mRoots.findRoot(rootUri);
+            final RootInfo root = mRoots.findRoot(rootUri);
             if (root != null) {
                 onRootPicked(root, true);
             } else {
@@ -252,7 +252,7 @@
             mDrawerToggle.setDrawerIndicatorEnabled(true);
 
         } else {
-            final DocumentRoot root = getCurrentRoot();
+            final RootInfo root = getCurrentRoot();
             actionBar.setIcon(root != null ? root.loadIcon(this) : null);
 
             if (mRoots.isRecentsRoot(root)) {
@@ -317,7 +317,7 @@
         super.onPrepareOptionsMenu(menu);
 
         final FragmentManager fm = getFragmentManager();
-        final Document cwd = getCurrentDirectory();
+        final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem search = menu.findItem(R.id.menu_search);
@@ -473,7 +473,7 @@
         }
     };
 
-    public DocumentRoot getCurrentRoot() {
+    public RootInfo getCurrentRoot() {
         if (mStack.size() > 0) {
             return mStack.getRoot(mRoots);
         } else {
@@ -481,7 +481,7 @@
         }
     }
 
-    public Document getCurrentDirectory() {
+    public DocumentInfo getCurrentDirectory() {
         return mStack.peek();
     }
 
@@ -491,7 +491,7 @@
 
     private void onCurrentDirectoryChanged() {
         final FragmentManager fm = getFragmentManager();
-        final Document cwd = getCurrentDirectory();
+        final DocumentInfo cwd = getCurrentDirectory();
 
         if (cwd == null) {
             // No directory means recents
@@ -533,14 +533,14 @@
         onCurrentDirectoryChanged();
     }
 
-    public void onRootPicked(DocumentRoot root, boolean closeDrawer) {
+    public void onRootPicked(RootInfo root, boolean closeDrawer) {
         // Clear entire backstack and start in new root
         mStack.clear();
 
         if (!mRoots.isRecentsRoot(root)) {
             try {
-                final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.docId);
-                onDocumentPicked(Document.fromUri(getContentResolver(), uri));
+                final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
+                onDocumentPicked(DocumentInfo.fromUri(getContentResolver(), uri));
             } catch (FileNotFoundException e) {
             }
         } else {
@@ -561,7 +561,7 @@
         finish();
     }
 
-    public void onDocumentPicked(Document doc) {
+    public void onDocumentPicked(DocumentInfo doc) {
         final FragmentManager fm = getFragmentManager();
         if (doc.isDirectory()) {
             // TODO: query display mode user preference for this dir
@@ -591,7 +591,7 @@
         }
     }
 
-    public void onDocumentsPicked(List<Document> docs) {
+    public void onDocumentsPicked(List<DocumentInfo> docs) {
         if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
             final int size = docs.size();
             final Uri[] uris = new Uri[size];
@@ -602,21 +602,19 @@
         }
     }
 
-    public void onSaveRequested(Document replaceTarget) {
+    public void onSaveRequested(DocumentInfo replaceTarget) {
         onFinished(replaceTarget.uri);
     }
 
     public void onSaveRequested(String mimeType, String displayName) {
-        final Document cwd = getCurrentDirectory();
+        final DocumentInfo cwd = getCurrentDirectory();
         final String authority = cwd.uri.getAuthority();
 
         final ContentProviderClient client = getContentResolver()
                 .acquireUnstableContentProviderClient(authority);
         try {
-            final String docId = DocumentsContract.createDocument(client,
-                    DocumentsContract.getDocId(cwd.uri), mimeType, displayName);
-
-            final Uri childUri = DocumentsContract.buildDocumentUri(authority, docId);
+            final Uri childUri = DocumentsContract.createDocument(
+                    getContentResolver(), cwd.uri, mimeType, displayName);
             onFinished(childUri);
         } catch (Exception e) {
             Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
@@ -701,7 +699,7 @@
 
     private void dumpStack() {
         Log.d(TAG, "Current stack:");
-        for (Document doc : mStack) {
+        for (DocumentInfo doc : mStack) {
             Log.d(TAG, "--> " + doc);
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
index a9929de..15ad061 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
@@ -16,10 +16,10 @@
 
 package com.android.documentsui;
 
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
 import com.android.internal.util.Predicate;
 
-public class MimePredicate implements Predicate<Document> {
+public class MimePredicate implements Predicate<DocumentInfo> {
     private final String[] mFilters;
 
     public MimePredicate(String[] filters) {
@@ -27,7 +27,7 @@
     }
 
     @Override
-    public boolean apply(Document doc) {
+    public boolean apply(DocumentInfo doc) {
         if (doc.isDirectory()) {
             return true;
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 3447a51..f5d87a7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -29,7 +29,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.provider.DocumentsContract.DocumentRoot;
 import android.text.TextUtils.TruncateAt;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -43,6 +42,7 @@
 import android.widget.TextView;
 
 import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.RootInfo;
 import com.google.android.collect.Lists;
 
 import libcore.io.IoUtils;
@@ -181,7 +181,7 @@
             final View summaryList = convertView.findViewById(R.id.summary_list);
 
             final DocumentStack stack = getItem(position);
-            final DocumentRoot root = stack.getRoot(roots);
+            final RootInfo root = stack.getRoot(roots);
             icon.setImageDrawable(root.loadIcon(context));
 
             final StringBuilder builder = new StringBuilder();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index aa21457..880a92b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -25,17 +25,21 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
+import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.DocumentRoot;
-import android.provider.DocumentsContract.Documents;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
 import android.util.Log;
 
+import com.android.documentsui.model.RootInfo;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Objects;
 import com.google.android.collect.Lists;
 
+import libcore.io.IoUtils;
+
 import java.util.List;
 
 /**
@@ -48,9 +52,9 @@
 
     private final Context mContext;
 
-    public List<DocumentRoot> mRoots = Lists.newArrayList();
+    public List<RootInfo> mRoots = Lists.newArrayList();
 
-    private DocumentRoot mRecentsRoot;
+    private RootInfo mRecentsRoot;
 
     public RootsCache(Context context) {
         mContext = context;
@@ -66,12 +70,10 @@
 
         {
             // Create special root for recents
-            final DocumentRoot root = new DocumentRoot();
-            root.rootType = DocumentRoot.ROOT_TYPE_SHORTCUT;
-            root.docId = null;
+            final RootInfo root = new RootInfo();
+            root.rootType = Root.ROOT_TYPE_SHORTCUT;
             root.icon = R.drawable.ic_dir;
             root.title = mContext.getString(R.string.root_recent);
-            root.summary = null;
             root.availableBytes = -1;
 
             mRoots.add(root);
@@ -89,28 +91,32 @@
 
                 // TODO: remove deprecated customRoots flag
                 // TODO: populate roots on background thread, and cache results
+                final Uri rootsUri = DocumentsContract.buildRootsUri(info.authority);
                 final ContentProviderClient client = resolver
                         .acquireUnstableContentProviderClient(info.authority);
+                Cursor cursor = null;
                 try {
-                    final List<DocumentRoot> roots = DocumentsContract.getDocumentRoots(client);
-                    for (DocumentRoot root : roots) {
-                        root.authority = info.authority;
+                    cursor = client.query(rootsUri, null, null, null, null);
+                    while (cursor.moveToNext()) {
+                        final RootInfo root = RootInfo.fromRootsCursor(info.authority, cursor);
+                        mRoots.add(root);
                     }
-                    mRoots.addAll(roots);
                 } catch (Exception e) {
                     Log.w(TAG, "Failed to load some roots from " + info.authority + ": " + e);
                 } finally {
+                    IoUtils.closeQuietly(cursor);
                     ContentProviderClient.closeQuietly(client);
                 }
             }
         }
     }
 
-    public DocumentRoot findRoot(Uri uri) {
+    @Deprecated
+    public RootInfo findRoot(Uri uri) {
         final String authority = uri.getAuthority();
-        final String docId = DocumentsContract.getDocId(uri);
-        for (DocumentRoot root : mRoots) {
-            if (Objects.equal(root.authority, authority) && Objects.equal(root.docId, docId)) {
+        final String docId = DocumentsContract.getDocumentId(uri);
+        for (RootInfo root : mRoots) {
+            if (Objects.equal(root.authority, authority) && Objects.equal(root.documentId, docId)) {
                 return root;
             }
         }
@@ -118,23 +124,23 @@
     }
 
     @GuardedBy("ActivityThread")
-    public DocumentRoot getRecentsRoot() {
+    public RootInfo getRecentsRoot() {
         return mRecentsRoot;
     }
 
     @GuardedBy("ActivityThread")
-    public boolean isRecentsRoot(DocumentRoot root) {
+    public boolean isRecentsRoot(RootInfo root) {
         return mRecentsRoot == root;
     }
 
     @GuardedBy("ActivityThread")
-    public List<DocumentRoot> getRoots() {
+    public List<RootInfo> getRoots() {
         return mRoots;
     }
 
     @GuardedBy("ActivityThread")
     public static Drawable resolveDocumentIcon(Context context, String mimeType) {
-        if (Documents.MIME_TYPE_DIR.equals(mimeType)) {
+        if (Document.MIME_TYPE_DIR.equals(mimeType)) {
             return context.getResources().getDrawable(R.drawable.ic_dir);
         } else {
             final PackageManager pm = context.getPackageManager();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 2cfa841..3102b88 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -26,7 +26,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
-import android.provider.DocumentsContract.DocumentRoot;
+import android.provider.DocumentsContract.Root;
 import android.text.format.Formatter;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -40,7 +40,8 @@
 import android.widget.TextView;
 
 import com.android.documentsui.SectionedListAdapter.SectionAdapter;
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.RootInfo;
 
 import java.util.Comparator;
 import java.util.List;
@@ -101,8 +102,8 @@
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
             final Object item = mAdapter.getItem(position);
-            if (item instanceof DocumentRoot) {
-                activity.onRootPicked((DocumentRoot) item, true);
+            if (item instanceof RootInfo) {
+                activity.onRootPicked((RootInfo) item, true);
             } else if (item instanceof ResolveInfo) {
                 activity.onAppPicked((ResolveInfo) item);
             } else {
@@ -111,7 +112,7 @@
         }
     };
 
-    private static class RootsAdapter extends ArrayAdapter<DocumentRoot> implements SectionAdapter {
+    private static class RootsAdapter extends ArrayAdapter<RootInfo> implements SectionAdapter {
         private int mHeaderId;
 
         public RootsAdapter(Context context, int headerId) {
@@ -131,15 +132,13 @@
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
 
-            final DocumentRoot root = getItem(position);
+            final RootInfo root = getItem(position);
             icon.setImageDrawable(root.loadIcon(context));
             title.setText(root.title);
 
             // Device summary is always available space
             final String summaryText;
-            if ((root.rootType == DocumentRoot.ROOT_TYPE_DEVICE
-                    || root.rootType == DocumentRoot.ROOT_TYPE_DEVICE_ADVANCED)
-                    && root.availableBytes >= 0) {
+            if (root.rootType == Root.ROOT_TYPE_DEVICE && root.availableBytes >= 0) {
                 summaryText = context.getString(R.string.root_available_bytes,
                         Formatter.formatFileSize(context, root.availableBytes));
             } else {
@@ -215,28 +214,27 @@
         private final RootsAdapter mDevicesAdvanced;
         private final AppsAdapter mApps;
 
-        public SectionedRootsAdapter(Context context, List<DocumentRoot> roots, Intent includeApps) {
+        public SectionedRootsAdapter(Context context, List<RootInfo> roots, Intent includeApps) {
             mServices = new RootsAdapter(context, R.string.root_type_service);
             mShortcuts = new RootsAdapter(context, R.string.root_type_shortcut);
             mDevices = new RootsAdapter(context, R.string.root_type_device);
             mDevicesAdvanced = new RootsAdapter(context, R.string.root_type_device);
             mApps = new AppsAdapter(context);
 
-            for (DocumentRoot root : roots) {
+            for (RootInfo root : roots) {
                 Log.d(TAG, "Found rootType=" + root.rootType);
                 switch (root.rootType) {
-                    case DocumentRoot.ROOT_TYPE_SERVICE:
+                    case Root.ROOT_TYPE_SERVICE:
                         mServices.add(root);
                         break;
-                    case DocumentRoot.ROOT_TYPE_SHORTCUT:
+                    case Root.ROOT_TYPE_SHORTCUT:
                         mShortcuts.add(root);
                         break;
-                    case DocumentRoot.ROOT_TYPE_DEVICE:
-                        mDevices.add(root);
+                    case Root.ROOT_TYPE_DEVICE:
                         mDevicesAdvanced.add(root);
-                        break;
-                    case DocumentRoot.ROOT_TYPE_DEVICE_ADVANCED:
-                        mDevicesAdvanced.add(root);
+                        if ((root.flags & Root.FLAG_ADVANCED) == 0) {
+                            mDevices.add(root);
+                        }
                         break;
                 }
             }
@@ -281,14 +279,14 @@
         }
     }
 
-    public static class RootComparator implements Comparator<DocumentRoot> {
+    public static class RootComparator implements Comparator<RootInfo> {
         @Override
-        public int compare(DocumentRoot lhs, DocumentRoot rhs) {
-            final int score = Document.compareToIgnoreCaseNullable(lhs.title, rhs.title);
+        public int compare(RootInfo lhs, RootInfo rhs) {
+            final int score = DocumentInfo.compareToIgnoreCaseNullable(lhs.title, rhs.title);
             if (score != 0) {
                 return score;
             } else {
-                return Document.compareToIgnoreCaseNullable(lhs.summary, rhs.summary);
+                return DocumentInfo.compareToIgnoreCaseNullable(lhs.summary, rhs.summary);
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index 7e1a297..8b0a974 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -31,7 +31,7 @@
 import android.widget.EditText;
 import android.widget.ImageView;
 
-import com.android.documentsui.model.Document;
+import com.android.documentsui.model.DocumentInfo;
 
 /**
  * Display document title editor and save button.
@@ -39,7 +39,7 @@
 public class SaveFragment extends Fragment {
     public static final String TAG = "SaveFragment";
 
-    private Document mReplaceTarget;
+    private DocumentInfo mReplaceTarget;
     private EditText mDisplayName;
     private Button mSave;
     private boolean mIgnoreNextEdit;
@@ -128,7 +128,7 @@
      * without changing the filename. Can be set to {@code null} if user
      * navigates outside the target directory.
      */
-    public void setReplaceTarget(Document replaceTarget) {
+    public void setReplaceTarget(DocumentInfo replaceTarget) {
         mReplaceTarget = replaceTarget;
 
         if (mReplaceTarget != null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/Document.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
similarity index 69%
rename from packages/DocumentsUI/src/com/android/documentsui/model/Document.java
rename to packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 692d171..f6e46a8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/Document.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -20,8 +20,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.DocumentColumns;
-import android.provider.DocumentsContract.Documents;
+import android.provider.DocumentsContract.Document;
 
 import com.android.documentsui.RecentsProvider;
 
@@ -31,9 +30,9 @@
 import java.util.Comparator;
 
 /**
- * Representation of a single document.
+ * Representation of a {@link Document}.
  */
-public class Document {
+public class DocumentInfo {
     public final Uri uri;
     public final String mimeType;
     public final String displayName;
@@ -42,7 +41,7 @@
     public final String summary;
     public final long size;
 
-    private Document(Uri uri, String mimeType, String displayName, long lastModified, int flags,
+    private DocumentInfo(Uri uri, String mimeType, String displayName, long lastModified, int flags,
             String summary, long size) {
         this.uri = uri;
         this.mimeType = mimeType;
@@ -53,23 +52,23 @@
         this.size = size;
     }
 
-    public static Document fromDirectoryCursor(Uri parent, Cursor cursor) {
+    public static DocumentInfo fromDirectoryCursor(Uri parent, Cursor cursor) {
         final String authority = parent.getAuthority();
-        final String docId = getCursorString(cursor, DocumentColumns.DOC_ID);
+        final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
 
         final Uri uri = DocumentsContract.buildDocumentUri(authority, docId);
-        final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
-        final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
-        final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
-        final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
-        final String summary = getCursorString(cursor, DocumentColumns.SUMMARY);
-        final long size = getCursorLong(cursor, DocumentColumns.SIZE);
+        final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+        final String displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+        final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+        final int flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+        final String summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+        final long size = getCursorLong(cursor, Document.COLUMN_SIZE);
 
-        return new Document(uri, mimeType, displayName, lastModified, flags, summary, size);
+        return new DocumentInfo(uri, mimeType, displayName, lastModified, flags, summary, size);
     }
 
     @Deprecated
-    public static Document fromRecentOpenCursor(ContentResolver resolver, Cursor recentCursor)
+    public static DocumentInfo fromRecentOpenCursor(ContentResolver resolver, Cursor recentCursor)
             throws FileNotFoundException {
         final Uri uri = Uri.parse(getCursorString(recentCursor, RecentsProvider.COL_URI));
         final long lastModified = getCursorLong(recentCursor, RecentsProvider.COL_TIMESTAMP);
@@ -80,14 +79,14 @@
             if (!cursor.moveToFirst()) {
                 throw new FileNotFoundException("Missing details for " + uri);
             }
-            final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
-            final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
-            final int flags = getCursorInt(cursor, DocumentColumns.FLAGS)
-                    & Documents.FLAG_SUPPORTS_THUMBNAIL;
-            final String summary = getCursorString(cursor, DocumentColumns.SUMMARY);
-            final long size = getCursorLong(cursor, DocumentColumns.SIZE);
+            final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+            final String displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+            final int flags = getCursorInt(cursor, Document.COLUMN_FLAGS)
+                    & Document.FLAG_SUPPORTS_THUMBNAIL;
+            final String summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+            final long size = getCursorLong(cursor, Document.COLUMN_SIZE);
 
-            return new Document(uri, mimeType, displayName, lastModified, flags, summary, size);
+            return new DocumentInfo(uri, mimeType, displayName, lastModified, flags, summary, size);
         } catch (Throwable t) {
             throw asFileNotFoundException(t);
         } finally {
@@ -95,21 +94,21 @@
         }
     }
 
-    public static Document fromUri(ContentResolver resolver, Uri uri) throws FileNotFoundException {
+    public static DocumentInfo fromUri(ContentResolver resolver, Uri uri) throws FileNotFoundException {
         Cursor cursor = null;
         try {
             cursor = resolver.query(uri, null, null, null, null);
             if (!cursor.moveToFirst()) {
                 throw new FileNotFoundException("Missing details for " + uri);
             }
-            final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
-            final String displayName = getCursorString(cursor, DocumentColumns.DISPLAY_NAME);
-            final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
-            final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
-            final String summary = getCursorString(cursor, DocumentColumns.SUMMARY);
-            final long size = getCursorLong(cursor, DocumentColumns.SIZE);
+            final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+            final String displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
+            final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+            final int flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+            final String summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
+            final long size = getCursorLong(cursor, Document.COLUMN_SIZE);
 
-            return new Document(uri, mimeType, displayName, lastModified, flags, summary, size);
+            return new DocumentInfo(uri, mimeType, displayName, lastModified, flags, summary, size);
         } catch (Throwable t) {
             throw asFileNotFoundException(t);
         } finally {
@@ -123,30 +122,30 @@
     }
 
     public boolean isCreateSupported() {
-        return (flags & Documents.FLAG_SUPPORTS_CREATE) != 0;
+        return (flags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0;
     }
 
     public boolean isSearchSupported() {
-        return (flags & Documents.FLAG_SUPPORTS_SEARCH) != 0;
+        return (flags & Document.FLAG_DIR_SUPPORTS_SEARCH) != 0;
     }
 
     public boolean isThumbnailSupported() {
-        return (flags & Documents.FLAG_SUPPORTS_THUMBNAIL) != 0;
+        return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
     }
 
     public boolean isDirectory() {
-        return Documents.MIME_TYPE_DIR.equals(mimeType);
+        return Document.MIME_TYPE_DIR.equals(mimeType);
     }
 
     public boolean isGridPreferred() {
-        return (flags & Documents.FLAG_PREFERS_GRID) != 0;
+        return (flags & Document.FLAG_DIR_PREFERS_GRID) != 0;
     }
 
     public boolean isDeleteSupported() {
-        return (flags & Documents.FLAG_SUPPORTS_DELETE) != 0;
+        return (flags & Document.FLAG_SUPPORTS_DELETE) != 0;
     }
 
-    private static String getCursorString(Cursor cursor, String columnName) {
+    public static String getCursorString(Cursor cursor, String columnName) {
         final int index = cursor.getColumnIndex(columnName);
         return (index != -1) ? cursor.getString(index) : null;
     }
@@ -154,7 +153,7 @@
     /**
      * Missing or null values are returned as -1.
      */
-    private static long getCursorLong(Cursor cursor, String columnName) {
+    public static long getCursorLong(Cursor cursor, String columnName) {
         final int index = cursor.getColumnIndex(columnName);
         if (index == -1) return -1;
         final String value = cursor.getString(index);
@@ -166,14 +165,14 @@
         }
     }
 
-    private static int getCursorInt(Cursor cursor, String columnName) {
+    public static int getCursorInt(Cursor cursor, String columnName) {
         final int index = cursor.getColumnIndex(columnName);
         return (index != -1) ? cursor.getInt(index) : 0;
     }
 
-    public static class DisplayNameComparator implements Comparator<Document> {
+    public static class DisplayNameComparator implements Comparator<DocumentInfo> {
         @Override
-        public int compare(Document lhs, Document rhs) {
+        public int compare(DocumentInfo lhs, DocumentInfo rhs) {
             final boolean leftDir = lhs.isDirectory();
             final boolean rightDir = rhs.isDirectory();
 
@@ -185,16 +184,16 @@
         }
     }
 
-    public static class LastModifiedComparator implements Comparator<Document> {
+    public static class LastModifiedComparator implements Comparator<DocumentInfo> {
         @Override
-        public int compare(Document lhs, Document rhs) {
+        public int compare(DocumentInfo lhs, DocumentInfo rhs) {
             return Long.compare(rhs.lastModified, lhs.lastModified);
         }
     }
 
-    public static class SizeComparator implements Comparator<Document> {
+    public static class SizeComparator implements Comparator<DocumentInfo> {
         @Override
-        public int compare(Document lhs, Document rhs) {
+        public int compare(DocumentInfo lhs, DocumentInfo rhs) {
             return Long.compare(rhs.size, lhs.size);
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index 81f75d2..b123a46 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -17,11 +17,10 @@
 package com.android.documentsui.model;
 
 import static com.android.documentsui.DocumentsActivity.TAG;
-import static com.android.documentsui.model.Document.asFileNotFoundException;
+import static com.android.documentsui.model.DocumentInfo.asFileNotFoundException;
 
 import android.content.ContentResolver;
 import android.net.Uri;
-import android.provider.DocumentsContract.DocumentRoot;
 import android.util.Log;
 
 import com.android.documentsui.RootsCache;
@@ -33,10 +32,10 @@
 import java.util.LinkedList;
 
 /**
- * Representation of a stack of {@link Document}, usually the result of a
+ * Representation of a stack of {@link DocumentInfo}, usually the result of a
  * user-driven traversal.
  */
-public class DocumentStack extends LinkedList<Document> {
+public class DocumentStack extends LinkedList<DocumentInfo> {
 
     public static String serialize(DocumentStack stack) {
         final JSONArray json = new JSONArray();
@@ -55,7 +54,7 @@
             final JSONArray json = new JSONArray(raw);
             for (int i = 0; i < json.length(); i++) {
                 final Uri uri = Uri.parse(json.getString(i));
-                final Document doc = Document.fromUri(resolver, uri);
+                final DocumentInfo doc = DocumentInfo.fromUri(resolver, uri);
                 stack.add(doc);
             }
         } catch (JSONException e) {
@@ -66,7 +65,7 @@
         return stack;
     }
 
-    public DocumentRoot getRoot(RootsCache roots) {
+    public RootInfo getRoot(RootsCache roots) {
         return roots.findRoot(getLast().uri);
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
new file mode 100644
index 0000000..f7027f3
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.model;
+
+import static com.android.documentsui.model.DocumentInfo.getCursorInt;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.provider.DocumentsContract.Root;
+
+/**
+ * Representation of a {@link Root}.
+ */
+public class RootInfo {
+    public String authority;
+    public String rootId;
+    public int rootType;
+    public int flags;
+    public int icon;
+    public String title;
+    public String summary;
+    public String documentId;
+    public long availableBytes;
+
+    public static RootInfo fromRootsCursor(String authority, Cursor cursor) {
+        final RootInfo root = new RootInfo();
+        root.authority = authority;
+        root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
+        root.rootType = getCursorInt(cursor, Root.COLUMN_ROOT_TYPE);
+        root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
+        root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
+        root.title = getCursorString(cursor, Root.COLUMN_TITLE);
+        root.summary = getCursorString(cursor, Root.COLUMN_SUMMARY);
+        root.documentId = getCursorString(cursor, Root.COLUMN_DOCUMENT_ID);
+        root.availableBytes = getCursorLong(cursor, Root.COLUMN_AVAILABLE_BYTES);
+        return root;
+    }
+
+    public Drawable loadIcon(Context context) {
+        if (icon != 0) {
+            if (authority != null) {
+                final PackageManager pm = context.getPackageManager();
+                final ProviderInfo info = pm.resolveContentProvider(authority, 0);
+                if (info != null) {
+                    return pm.getDrawable(info.packageName, icon, info.applicationInfo);
+                }
+            } else {
+                return context.getResources().getDrawable(icon);
+            }
+        }
+        return null;
+    }
+}