Start fleshing out new storage APIs.

Introduces new DocumentsContract which storage backends must
implement.  Backends surface a simple directory-like organizational
structure that enables a document to appear at multiple locations in
that hierarchy.  Querying a document or the contents of a directory
will return a Cursor populated with DocumentColumns, which includes
simple metadata.

Adds new OPEN_DOC and CREATE_DOC Intents, and permission to protect
storage backends.

Change-Id: Ib4984bc980182b2cedbe552908e5be94604ef085
diff --git a/api/current.txt b/api/current.txt
index c1c550d..a9f0ef8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -70,6 +70,7 @@
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
     field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
+    field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
     field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
@@ -6047,6 +6048,7 @@
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+    field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
     field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
     field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
@@ -6091,6 +6093,7 @@
     field public static final java.lang.String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED";
     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_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";
@@ -19494,6 +19497,28 @@
     field public static final android.net.Uri CONTENT_URI;
   }
 
+  public final class DocumentsContract {
+    ctor public DocumentsContract();
+    method public static android.net.Uri buildContentsUri(android.net.Uri);
+    method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String);
+    method public static android.net.Uri buildSearchUri(java.lang.String, java.lang.String);
+    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point);
+    method public static boolean renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
+    field public static final java.lang.String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
+    field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
+    field public static final int FLAG_SUPPORTS_RENAME = 2; // 0x2
+    field public static final int FLAG_SUPPORTS_THUMBNAIL = 4; // 0x4
+    field public static final java.lang.String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc";
+    field public static final java.lang.String ROOT_GUID = "0";
+  }
+
+  public static abstract interface DocumentsContract.DocumentColumns implements android.provider.OpenableColumns {
+    field public static final java.lang.String FLAGS = "flags";
+    field public static final java.lang.String GUID = "guid";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
+    field public static final java.lang.String MIME_TYPE = "mime_type";
+  }
+
   public final deprecated class LiveFolders implements android.provider.BaseColumns {
     field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
     field public static final java.lang.String DESCRIPTION = "description";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5fa1a6c..621bcee 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2591,6 +2591,46 @@
      */
     public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
 
+    /**
+     * Activity Action: Allow the user to select and open one or more existing
+     * documents. Both read and write access to the documents will be granted
+     * until explicitly revoked by the user.
+     * <p>
+     * Callers can restrict selection to a specific kind of data, such as
+     * photos, by setting one or more MIME types in {@link #EXTRA_MIME_TYPES}.
+     * <p>
+     * If the caller can handle multiple returned items (the user performing
+     * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} to
+     * indicate this.
+     * <p>
+     * All returned URIs can be opened as a stream with
+     * {@link ContentResolver#openInputStream(Uri)}.
+     * <p>
+     * Output: The URI of the item that was picked. This must be a content: URI
+     * so that any receiver can access it.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
+
+    /**
+     * Activity Action: Allow the user to create a new document. Both read and
+     * write access to the document will be granted until explicitly revoked by
+     * the user.
+     * <p>
+     * Callers can provide a hint document name by setting {@link #EXTRA_TITLE},
+     * but the user may change this value before creating the file. Callers can
+     * optionally hint at the MIME type being created by setting
+     * {@link #setType(String)}.
+     * <p>
+     * All returned URIs can be opened as a stream with
+     * {@link ContentResolver#openOutputStream(Uri)}.
+     * <p>
+     * Output: The URI of the item that was created. This must be a content: URI
+     * so that any receiver can access it.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
@@ -3194,6 +3234,14 @@
     public static final String EXTRA_RESTRICTIONS_INTENT =
             "android.intent.extra.restrictions_intent";
 
+    /**
+     * Extra used to communicate set of acceptable MIME types for
+     * {@link #ACTION_GET_CONTENT} or {@link #ACTION_OPEN_DOC}. The type of the
+     * extra is <code>ArrayList&lt;String&gt;</code>.
+     * @hide
+     */
+    public static final String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
new file mode 100644
index 0000000..e10ead9
--- /dev/null
+++ b/core/java/android/provider/DocumentsContract.java
@@ -0,0 +1,207 @@
+/*
+ * 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 android.provider;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The contract between a storage backend and the platform. Contains definitions
+ * for the supported URIs and columns.
+ */
+public final class DocumentsContract {
+    private static final String TAG = "Documents";
+
+    // content://com.example/docs/0/
+    // content://com.example/docs/0/contents/
+    // content://com.example/search/?query=pony
+
+    /**
+     * MIME type of a document which is a directory that may contain additional
+     * documents.
+     *
+     * @see #buildContentsUri(Uri)
+     */
+    public static final String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc";
+
+    /**
+     * {@link DocumentColumns#GUID} value representing the root directory of a
+     * storage backend.
+     */
+    public static final String ROOT_GUID = "0";
+
+    /**
+     * Flag indicating that a document is a directory that supports creation of
+     * new files within it.
+     *
+     * @see DocumentColumns#FLAGS
+     * @see #buildContentsUri(Uri)
+     */
+    public static final int FLAG_SUPPORTS_CREATE = 1;
+
+    /**
+     * Flag indicating that a document is renamable.
+     *
+     * @see DocumentColumns#FLAGS
+     * @see #renameDocument(ContentResolver, Uri, String)
+     */
+    public static final int FLAG_SUPPORTS_RENAME = 1 << 1;
+
+    /**
+     * Flag indicating that a document can be represented as a thumbnail.
+     *
+     * @see DocumentColumns#FLAGS
+     * @see #getThumbnail(ContentResolver, Uri, Point)
+     */
+    public static final int FLAG_SUPPORTS_THUMBNAIL = 1 << 2;
+
+    /**
+     * Optimal dimensions for a document thumbnail request, stored as a
+     * {@link Point} object. This is only a hint, and the returned thumbnail may
+     * have different dimensions.
+     */
+    public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
+
+    private static final String PATH_DOCS = "docs";
+    private static final String PATH_CONTENTS = "contents";
+    private static final String PATH_SEARCH = "search";
+
+    private static final String PARAM_QUERY = "query";
+
+    /**
+     * Build URI representing the given {@link DocumentColumns#GUID} in a
+     * storage backend.
+     */
+    public static Uri buildDocumentUri(String authority, String guid) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority).appendPath(PATH_DOCS).appendPath(guid).build();
+    }
+
+    /**
+     * Build URI representing a search for matching documents in a storage
+     * backend.
+     */
+    public static Uri buildSearchUri(String authority, String query) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
+                .appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build();
+    }
+
+    /**
+     * Build URI representing the contents of the given directory in a storage
+     * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}.
+     */
+    public static Uri buildContentsUri(Uri documentUri) {
+        return documentUri.buildUpon().appendPath(PATH_CONTENTS).build();
+    }
+
+    /**
+     * These are standard columns for document URIs. Storage backend providers
+     * <em>must</em> support at least these columns when queried.
+     *
+     * @see Intent#ACTION_OPEN_DOCUMENT
+     * @see Intent#ACTION_CREATE_DOCUMENT
+     */
+    public interface DocumentColumns extends OpenableColumns {
+        /**
+         * The globally unique ID for a document within a storage backend.
+         * Values <em>must</em> never change once returned.
+         * <p>
+         * Type: STRING
+         *
+         * @see DocumentsContract#ROOT_GUID
+         */
+        public static final String GUID = "guid";
+
+        /**
+         * MIME type of a document, matching the value returned by
+         * {@link ContentResolver#getType(android.net.Uri)}.
+         * <p>
+         * Type: STRING
+         *
+         * @see DocumentsContract#MIME_TYPE_DIRECTORY
+         */
+        public static final String MIME_TYPE = "mime_type";
+
+        /**
+         * Timestamp when a document was last modified, in milliseconds since
+         * January 1, 1970 00:00:00.0 UTC.
+         * <p>
+         * Type: INTEGER (long)
+         *
+         * @see System#currentTimeMillis()
+         */
+        public static final String LAST_MODIFIED = "last_modified";
+
+        /**
+         * Flags that apply to a specific document.
+         * <p>
+         * Type: INTEGER (int)
+         */
+        public static final String FLAGS = "flags";
+    }
+
+    /**
+     * Return thumbnail representing the document at the given URI. Callers are
+     * responsible for their own caching. Given document must have
+     * {@link #FLAG_SUPPORTS_THUMBNAIL} set.
+     *
+     * @return decoded thumbnail, or {@code null} if problem was encountered.
+     */
+    public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) {
+        final Bundle opts = new Bundle();
+        opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size);
+
+        InputStream is = null;
+        try {
+            is = new AssetFileDescriptor.AutoCloseInputStream(
+                    resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts));
+            return BitmapFactory.decodeStream(is);
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
+            return null;
+        } finally {
+            IoUtils.closeQuietly(is);
+        }
+    }
+
+    /**
+     * Rename the document at the given URI. Given document must have
+     * {@link #FLAG_SUPPORTS_RENAME} set.
+     *
+     * @return if rename was successful.
+     */
+    public static boolean renameDocument(
+            ContentResolver resolver, Uri documentUri, String displayName) {
+        final ContentValues values = new ContentValues();
+        values.put(DocumentColumns.DISPLAY_NAME, displayName);
+        return (resolver.update(documentUri, values, null, null) == 1);
+    }
+}
diff --git a/core/java/android/provider/OpenableColumns.java b/core/java/android/provider/OpenableColumns.java
index f548bae..faf96b7 100644
--- a/core/java/android/provider/OpenableColumns.java
+++ b/core/java/android/provider/OpenableColumns.java
@@ -16,11 +16,17 @@
 
 package android.provider;
 
+import android.content.ContentResolver;
+import android.content.Intent;
+
 /**
- * These are standard columns for openable URIs. (See
- * {@link android.content.Intent#CATEGORY_OPENABLE}.) If possible providers that have openable URIs
- * should support these columns. To find the content type of a URI use
- * {@link android.content.ContentResolver#getType(android.net.Uri)} as normal.
+ * These are standard columns for openable URIs. Providers that serve openable
+ * URIs <em>must</em> support at least these columns when queried.
+ * <p>
+ * To find the content type of a URI, use
+ * {@link ContentResolver#getType(android.net.Uri)}.
+ *
+ * @see Intent#CATEGORY_OPENABLE
  */
 public interface OpenableColumns {
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 83d6061..8ef127a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1078,6 +1078,14 @@
         android:description="@string/permdesc_mediaStorageWrite"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to manage access to documents, usually as part
+         of a document picker. -->
+    <permission android:name="android.permission.MANAGE_DOCUMENTS"
+        android:permissionGroup="android.permission-group.STORAGE"
+        android:label="@string/permlab_manageDocs"
+        android:description="@string/permdesc_manageDocs"
+        android:protectionLevel="signature|system" />
+
     <!-- ================================== -->
     <!-- Permissions for screenlock         -->
     <!-- ================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 09be719..50fad5f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1797,6 +1797,11 @@
     <string name="permdesc_mediaStorageWrite" product="default">Allows the app to modify the contents of the internal media storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+    <string name="permlab_manageDocs" product="default">manage document storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_manageDocs" product="default">Allows the app to manage document storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
     <string name="permlab_sdcardAccessAll">access external storage of all users</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_sdcardAccessAll">Allows the app to access external storage for all users.</string>