Home | History | Annotate | Download | only in providers
      1 page.title=Storage Access Framework
      2 @jd:body
      3 <div id="qv-wrapper">
      4 <div id="qv">
      5 
      6 <h2>In this document
      7  <a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle">
      8         <span class="more">show more</span>
      9         <span class="less" style="display:none">show less</span></a></h2>
     10 <ol id="toc44" class="hide-nested">
     11     <li>
     12         <a href="#overview">Overview</a>
     13     </li>
     14     <li>
     15         <a href="#flow">Control Flow</a>
     16     </li>
     17     <li>
     18         <a href="#client">Writing a Client App</a>
     19         <ol>
     20         <li><a href="#search">Search for documents</a></li>
     21         <li><a href="#process">Process results</a></li>
     22         <li><a href="#metadata">Examine document metadata</a></li>
     23         <li><a href="#open">Open a document</a></li>
     24         <li><a href="#create">Create a new document</a></li>
     25         <li><a href="#delete">Delete a document</a></li>
     26         <li><a href="#edit">Edit a document</a></li>
     27         <li><a href="#permissions">Persist permissions</a></li>
     28         </ol>
     29     </li>
     30     <li><a href="#custom">Writing a Custom Document Provider</a>
     31         <ol>
     32         <li><a href="#manifest">Manifest</a></li>
     33         <li><a href="#contract">Contracts</a></li>
     34         <li><a href="#subclass">Subclass DocumentsProvider</a></li>
     35         <li><a href="#security">Security</a></li>
     36         </ol>
     37     </li>
     38 
     39 </ol>
     40 <h2>Key classes</h2>
     41 <ol>
     42     <li>{@link android.provider.DocumentsProvider}</li>
     43     <li>{@link android.provider.DocumentsContract}</li>
     44 </ol>
     45 
     46 <h2>Videos</h2>
     47 
     48 <ol>
     49     <li><a href="http://www.youtube.com/watch?v=zxHVeXbK1P4">
     50 DevBytes: Android 4.4 Storage Access Framework: Provider</a></li>
     51      <li><a href="http://www.youtube.com/watch?v=UFj9AEz0DHQ">
     52 DevBytes: Android 4.4 Storage Access Framework: Client</a></li>
     53 </ol>
     54 
     55 
     56 <h2>Code Samples</h2>
     57 
     58 <ol>
     59     <li><a href="{@docRoot}samples/StorageProvider/index.html">
     60 Storage Provider</a></li>
     61      <li><a href="{@docRoot}samples/StorageClient/index.html">
     62 StorageClient</a></li>
     63 </ol>
     64 
     65 <h2>See Also</h2>
     66 <ol>
     67     <li>
     68         <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
     69         Content Provider Basics
     70         </a>
     71     </li>
     72 </ol>
     73 
     74 </div>
     75 </div>
     76 
     77 
     78 <p>Android 4.4 (API level 19) introduces the Storage Access Framework (SAF). The SAF
     79  makes it simple for users to browse and open documents, images, and other files
     80 across all of their their preferred document storage providers. A standard, easy-to-use UI
     81 lets users browse files and access recents in a consistent way across apps and providers.</p>
     82 
     83 <p>Cloud or local storage services can participate in this ecosystem by implementing a
     84 {@link android.provider.DocumentsProvider} that encapsulates their services. Client
     85 apps that need access to a provider's documents can integrate with the SAF with just a few
     86 lines of code.</p>
     87 
     88 <p>The SAF includes the following:</p>
     89 
     90 <ul>
     91 <li><strong>Document provider</strong>&mdash;A content provider that allows a
     92 storage service (such as Google Drive) to reveal the files it manages. A document provider is
     93 implemented as a subclass of the {@link android.provider.DocumentsProvider} class.
     94 The document-provider schema is based on a traditional file hierarchy,
     95 though how your document provider physically stores data is up to you.
     96 The Android platform includes several built-in document providers, such as
     97 Downloads, Images, and Videos.</li>
     98 
     99 <li><strong>Client app</strong>&mdash;A custom app that invokes the
    100 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} and/or
    101 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} intent and receives the
    102 files returned by document providers.</li>
    103 
    104 <li><strong>Picker</strong>&mdash;A system UI that lets users access documents from all
    105 document providers that satisfy the client app's search criteria.</li>
    106 </ul>
    107 
    108 <p>Some of the features offered by the SAF are as follows:</p>
    109 <ul>
    110 <li>Lets users browse content from all document providers, not just a single app.</li>
    111 <li>Makes it possible for your app to have long term, persistent access to
    112  documents owned by a document provider. Through this access users can add, edit,
    113  save, and delete files on the provider.</li>
    114 <li>Supports multiple user accounts and transient roots such as USB storage
    115 providers, which only appear if the drive is plugged in. </li>
    116 </ul>
    117 
    118 <h2 id ="overview">Overview</h2>
    119 
    120 <p>The SAF centers around a content provider that is a
    121 subclass of the {@link android.provider.DocumentsProvider} class. Within a <em>document provider</em>, data is
    122 structured as a traditional file hierarchy:</p>
    123 <p><img src="{@docRoot}images/providers/storage_datamodel.png" alt="data model" /></p>
    124 <p class="img-caption"><strong>Figure 1.</strong> Document provider data model. A Root points to a single Document,
    125 which then starts the fan-out of the entire tree.</p>
    126 
    127 <p>Note the following:</p>
    128 <ul>
    129 
    130 <li>Each document provider reports one or more
    131 &quot;roots&quot; which are starting points into exploring a tree of documents.
    132 Each root has a unique {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID},
    133 and it points to a document (a directory)
    134 representing the contents under that root.
    135 Roots are dynamic by design to support use cases like multiple accounts,
    136 transient USB storage devices, or user login/log out.</li>
    137 
    138 <li>Under each root is a single document. That document points to 1 to <em>N</em> documents,
    139 each of which in turn can point to 1 to <em>N</em> documents. </li>
    140 
    141 <li>Each storage backend surfaces
    142 individual files and directories by referencing them with a unique
    143 {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID}.
    144 Document IDs must be unique and not change once issued, since they are used for persistent
    145 URI grants across device reboots.</li>
    146 
    147 
    148 <li>Documents can be either an openable file (with a specific MIME type), or a
    149 directory containing additional documents (with the
    150 {@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR} MIME type).</li>
    151 
    152 <li>Each document can have different capabilities, as described by
    153 {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS}.
    154 For example, {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE},
    155 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE}, and
    156 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}.
    157 The same {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} can be
    158 included in multiple directories.</li>
    159 </ul>
    160 
    161 <h2 id="flow">Control Flow</h2>
    162 <p>As stated above, the  document provider data model is based on a traditional
    163 file hierarchy. However, you can physically store your data however you like, as
    164 long as it can be accessed through the {@link android.provider.DocumentsProvider} API. For example, you
    165 could use tag-based cloud storage for your data.</p>
    166 
    167 <p>Figure 2 shows an example of how a photo app might use the SAF
    168 to access  stored data:</p>
    169 <p><img src="{@docRoot}images/providers/storage_dataflow.png" alt="app" /></p>
    170 
    171 <p class="img-caption"><strong>Figure 2.</strong> Storage Access Framework Flow</p>
    172 
    173 <p>Note the following:</p>
    174 <ul>
    175 
    176 <li>In the SAF, providers and clients don't interact
    177 directly. A client requests permission to interact
    178 with files (that is, to read, edit, create, or delete files).</li>
    179 
    180 <li>The interaction starts when an application (in this example, a photo app)  fires the intent
    181 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} or {@link android.content.Intent#ACTION_CREATE_DOCUMENT}. The intent may include filters
    182 to further refine the criteria&mdash;for example, &quot;give me all openable files
    183 that have the 'image' MIME type.&quot;</li>
    184 
    185 <li>Once the intent fires, the system picker goes to each registered provider
    186 and shows the user the matching content roots.</li>
    187 
    188 <li>The picker gives users a standard interface for accessing documents, even
    189 though the underlying document providers may be very different. For example, figure 2
    190 shows a Google Drive provider, a USB provider, and a cloud provider.</li>
    191 </ul>
    192 
    193 <p>Figure 3 shows a picker in which a user searching for images has selected a
    194 Google Drive account:</p>
    195 
    196 <p><img src="{@docRoot}images/providers/storage_picker.png" width="340"
    197 alt="picker" style="border:2px solid #ddd"/></p>
    198 
    199 <p class="img-caption"><strong>Figure 3.</strong> Picker</p>
    200 
    201 <p>When the user selects Google Drive the images are displayed, as shown in
    202 figure 4. From that point on, the user can interact with them in whatever ways
    203 are supported by the provider and client app.
    204 
    205 <p><img src="{@docRoot}images/providers/storage_photos.png" width="340"
    206 alt="picker" style="border:2px solid #ddd"/></p>
    207 
    208 <p class="img-caption"><strong>Figure 4.</strong> Images</p>
    209 
    210 <h2 id="client">Writing a Client App</h2>
    211 
    212 <p>On Android 4.3 and lower, if you want your app to retrieve a file from another
    213 app, it must invoke an intent such as {@link android.content.Intent#ACTION_PICK}
    214 or {@link android.content.Intent#ACTION_GET_CONTENT}. The user must then select
    215 a single app from which to pick a file and the selected app must provide a user
    216 interface for the user to browse and pick from the available files. </p>
    217 
    218 <p>On Android 4.4 and higher, you have the additional option of using the
    219 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} intent,
    220 which displays a picker UI controlled by the system that allows the user to
    221 browse all files that other apps have made available. From this single UI, the
    222 user can pick a file from any of the supported apps.</p>
    223 
    224 <p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT} is
    225 not intended to be a replacement for {@link android.content.Intent#ACTION_GET_CONTENT}.
    226 The one you should use depends on the needs of your app:</p>
    227 
    228 <ul>
    229 <li>Use {@link android.content.Intent#ACTION_GET_CONTENT} if you want your app
    230 to simply read/import data. With this approach, the app imports a copy of the data,
    231 such as an image file.</li>
    232 
    233 <li>Use {@link android.content.Intent#ACTION_OPEN_DOCUMENT} if you want your
    234 app to have long term, persistent access to documents owned by a document
    235 provider. An example would be a photo-editing app that lets users edit
    236 images stored in a document provider. </li>
    237 
    238 </ul>
    239 
    240 
    241 <p>This section describes how to write client apps based on the
    242 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} and
    243 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} intents.</p>
    244 
    245 
    246 <h3 id="search">Search for documents</h3>
    247 
    248 <p>
    249 The following snippet uses {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    250 to search for document providers that
    251 contain image files:</p>
    252 
    253 <pre>private static final int READ_REQUEST_CODE = 42;
    254 ...
    255 /**
    256  * Fires an intent to spin up the &quot;file chooser&quot; UI and select an image.
    257  */
    258 public void performFileSearch() {
    259 
    260     // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    261     // browser.
    262     Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    263 
    264     // Filter to only show results that can be &quot;opened&quot;, such as a
    265     // file (as opposed to a list of contacts or timezones)
    266     intent.addCategory(Intent.CATEGORY_OPENABLE);
    267 
    268     // Filter to show only images, using the image MIME data type.
    269     // If one wanted to search for ogg vorbis files, the type would be &quot;audio/ogg&quot;.
    270     // To search for all documents available via installed storage providers,
    271     // it would be &quot;*/*&quot;.
    272     intent.setType(&quot;image/*&quot;);
    273 
    274     startActivityForResult(intent, READ_REQUEST_CODE);
    275 }</pre>
    276 
    277 <p>Note the following:</p>
    278 <ul>
    279 <li>When the app fires the {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    280 intent, it launches a picker that displays all matching document providers.</li>
    281 
    282 <li>Adding the category {@link android.content.Intent#CATEGORY_OPENABLE} to the
    283 intent filters the results to display only documents that can be opened, such as image files.</li>
    284 
    285 <li>The statement {@code intent.setType(&quot;image/*&quot;)} further filters to
    286 display only documents that have the image MIME data type.</li>
    287 </ul>
    288 
    289 <h3 id="results">Process Results</h3>
    290 
    291 <p>Once the user selects a document in the picker,
    292 {@link android.app.Activity#onActivityResult onActivityResult()} gets called.
    293 The URI that points to the selected document is contained in the {@code resultData}
    294 parameter. Extract the URI using {@link android.content.Intent#getData getData()}.
    295 Once you have it, you can use it to retrieve the document the user wants. For
    296 example:</p>
    297 
    298 <pre>&#64;Override
    299 public void onActivityResult(int requestCode, int resultCode,
    300         Intent resultData) {
    301 
    302     // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    303     // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    304     // response to some other intent, and the code below shouldn't run at all.
    305 
    306     if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    307         // The document selected by the user won't be returned in the intent.
    308         // Instead, a URI to that document will be contained in the return intent
    309         // provided to this method as a parameter.
    310         // Pull that URI using resultData.getData().
    311         Uri uri = null;
    312         if (resultData != null) {
    313             uri = resultData.getData();
    314             Log.i(TAG, "Uri: " + uri.toString());
    315             showImage(uri);
    316         }
    317     }
    318 }
    319 </pre>
    320 
    321 <h3 id="metadata">Examine document metadata</h3>
    322 
    323 <p>Once you have the URI for a document, you gain access to its metadata. This
    324 snippet grabs the metadata for a document specified by the URI, and logs it:</p>
    325 
    326 <pre>public void dumpImageMetaData(Uri uri) {
    327 
    328     // The query, since it only applies to a single document, will only return
    329     // one row. There's no need to filter, sort, or select fields, since we want
    330     // all fields for one document.
    331     Cursor cursor = getActivity().getContentResolver()
    332             .query(uri, null, null, null, null, null);
    333 
    334     try {
    335     // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    336     // &quot;if there's anything to look at, look at it&quot; conditionals.
    337         if (cursor != null &amp;&amp; cursor.moveToFirst()) {
    338 
    339             // Note it's called &quot;Display Name&quot;.  This is
    340             // provider-specific, and might not necessarily be the file name.
    341             String displayName = cursor.getString(
    342                     cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
    343             Log.i(TAG, &quot;Display Name: &quot; + displayName);
    344 
    345             int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
    346             // If the size is unknown, the value stored is null.  But since an
    347             // int can't be null in Java, the behavior is implementation-specific,
    348             // which is just a fancy term for &quot;unpredictable&quot;.  So as
    349             // a rule, check if it's null before assigning to an int.  This will
    350             // happen often:  The storage API allows for remote files, whose
    351             // size might not be locally known.
    352             String size = null;
    353             if (!cursor.isNull(sizeIndex)) {
    354                 // Technically the column stores an int, but cursor.getString()
    355                 // will do the conversion automatically.
    356                 size = cursor.getString(sizeIndex);
    357             } else {
    358                 size = &quot;Unknown&quot;;
    359             }
    360             Log.i(TAG, &quot;Size: &quot; + size);
    361         }
    362     } finally {
    363         cursor.close();
    364     }
    365 }
    366 </pre>
    367 
    368 <h3 id="open-client">Open a document</h3>
    369 
    370 <p>Once you have the URI for a document, you can open it or do whatever else
    371 you want to do with it.</p>
    372 
    373 <h4>Bitmap</h4>
    374 
    375 <p>Here is an example of how you might open a {@link android.graphics.Bitmap}:</p>
    376 
    377 <pre>private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    378     ParcelFileDescriptor parcelFileDescriptor =
    379             getContentResolver().openFileDescriptor(uri, "r");
    380     FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    381     Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    382     parcelFileDescriptor.close();
    383     return image;
    384 }
    385 </pre>
    386 
    387 <p>Note that you should not do this operation on the UI thread. Do it in the
    388 background, using {@link android.os.AsyncTask}. Once you open the bitmap, you
    389 can display it in an {@link android.widget.ImageView}.
    390 </p>
    391 
    392 <h4>Get an InputStream</h4>
    393 
    394 <p>Here is an example of how you can get an {@link java.io.InputStream} from the URI. In this
    395 snippet, the lines of the file are being read into a string:</p>
    396 
    397 <pre>private String readTextFromUri(Uri uri) throws IOException {
    398     InputStream inputStream = getContentResolver().openInputStream(uri);
    399     BufferedReader reader = new BufferedReader(new InputStreamReader(
    400             inputStream));
    401     StringBuilder stringBuilder = new StringBuilder();
    402     String line;
    403     while ((line = reader.readLine()) != null) {
    404         stringBuilder.append(line);
    405     }
    406     fileInputStream.close();
    407     parcelFileDescriptor.close();
    408     return stringBuilder.toString();
    409 }
    410 </pre>
    411 
    412 <h3 id="create">Create a new document</h3>
    413 
    414 <p>Your app can create a new document in a document provider using the
    415 {@link android.content.Intent#ACTION_CREATE_DOCUMENT}
    416 intent. To create a file you give your intent a MIME type and a file name, and
    417 launch it with a unique request code. The rest is taken care of for you:</p>
    418 
    419 
    420 <pre>
    421 // Here are some examples of how you might call this method.
    422 // The first parameter is the MIME type, and the second parameter is the name
    423 // of the file you are creating:
    424 //
    425 // createFile("text/plain", "foobar.txt");
    426 // createFile("image/png", "mypicture.png");
    427 
    428 // Unique request code.
    429 private static final int WRITE_REQUEST_CODE = 43;
    430 ...
    431 private void createFile(String mimeType, String fileName) {
    432     Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    433 
    434     // Filter to only show results that can be &quot;opened&quot;, such as
    435     // a file (as opposed to a list of contacts or timezones).
    436     intent.addCategory(Intent.CATEGORY_OPENABLE);
    437 
    438     // Create a file with the requested MIME type.
    439     intent.setType(mimeType);
    440     intent.putExtra(Intent.EXTRA_TITLE, fileName);
    441     startActivityForResult(intent, WRITE_REQUEST_CODE);
    442 }
    443 </pre>
    444 
    445 <p>Once you create a new document you can get its URI in
    446 {@link android.app.Activity#onActivityResult onActivityResult()}, so that you
    447 can continue to write to it.</p>
    448 
    449 <h3 id="delete">Delete a document</h3>
    450 
    451 <p>If you have the URI for a document and the document's
    452 {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS}
    453 contains
    454 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE},
    455 you can delete the document. For example:</p>
    456 
    457 <pre>
    458 DocumentsContract.deleteDocument(getContentResolver(), uri);
    459 </pre>
    460 
    461 <h3 id="edit">Edit a document</h3>
    462 
    463 <p>You can use the SAF to edit a text document in place.
    464 This snippet fires
    465 the {@link android.content.Intent#ACTION_OPEN_DOCUMENT} intent and uses the
    466 category {@link android.content.Intent#CATEGORY_OPENABLE} to to display only
    467 documents that can be opened. It further filters to show only text files:</p>
    468 
    469 <pre>
    470 private static final int EDIT_REQUEST_CODE = 44;
    471 /**
    472  * Open a file for writing and append some text to it.
    473  */
    474  private void editDocument() {
    475     // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    476     // file browser.
    477     Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    478 
    479     // Filter to only show results that can be &quot;opened&quot;, such as a
    480     // file (as opposed to a list of contacts or timezones).
    481     intent.addCategory(Intent.CATEGORY_OPENABLE);
    482 
    483     // Filter to show only text files.
    484     intent.setType(&quot;text/plain&quot;);
    485 
    486     startActivityForResult(intent, EDIT_REQUEST_CODE);
    487 }
    488 </pre>
    489 
    490 <p>Next, from {@link android.app.Activity#onActivityResult onActivityResult()}
    491 (see <a href="#results">Process results</a>) you can call code to perform the edit.
    492 The following snippet gets a {@link java.io.FileOutputStream}
    493 from the {@link android.content.ContentResolver}. By default it uses write mode.
    494 It's best practice to ask for the least amount of access you need, so dont ask
    495 for read/write if all you need is write:</p>
    496 
    497 <pre>private void alterDocument(Uri uri) {
    498     try {
    499         ParcelFileDescriptor pfd = getActivity().getContentResolver().
    500                 openFileDescriptor(uri, "w");
    501         FileOutputStream fileOutputStream =
    502                 new FileOutputStream(pfd.getFileDescriptor());
    503         fileOutputStream.write(("Overwritten by MyCloud at " +
    504                 System.currentTimeMillis() + "\n").getBytes());
    505         // Let the document provider know you're done by closing the stream.
    506         fileOutputStream.close();
    507         pfd.close();
    508     } catch (FileNotFoundException e) {
    509         e.printStackTrace();
    510     } catch (IOException e) {
    511         e.printStackTrace();
    512     }
    513 }</pre>
    514 
    515 <h3 id="permissions">Persist permissions</h3>
    516 
    517 <p>When your app opens a file for reading or writing, the system gives your
    518 app a URI permission grant for that file. It lasts until the user's device restarts.
    519 But suppose your app is an image-editing app, and you want users to be able to
    520 access the last 5 images they edited, directly from your app. If the user's device has
    521 restarted, you'd have to send the user back to the system picker to find the
    522 files, which is obviously not ideal.</p>
    523 
    524 <p>To prevent this from happening, you can persist the permissions the system
    525 gives your app. Effectively, your app "takes" the persistable URI permission grant
    526 that the system is offering. This gives the user continued access to the files
    527 through your app, even if the device has been restarted:</p>
    528 
    529 
    530 <pre>final int takeFlags = intent.getFlags()
    531             &amp; (Intent.FLAG_GRANT_READ_URI_PERMISSION
    532             | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    533 // Check for the freshest data.
    534 getContentResolver().takePersistableUriPermission(uri, takeFlags);</pre>
    535 
    536 <p>There is one final step. You may have saved the most
    537 recent URIs your app accessed, but they may no longer be valid&mdash;another app
    538 may have deleted or modified a document. Thus, you should always call
    539 {@code getContentResolver().takePersistableUriPermission()} to check for the
    540 freshest data.</p>
    541 
    542 <h2 id="custom">Writing a Custom Document Provider</h2>
    543 
    544 <p>
    545 If you're developing an app that provides storage services for files (such as
    546 a cloud save service), you can make your files available through the
    547 SAF by writing a custom document provider.  This section describes
    548 how to do this.</p>
    549 
    550 
    551 <h3 id="manifest">Manifest</h3>
    552 
    553 <p>To implement a custom document provider, add the following to your application's
    554 manifest:</p>
    555 <ul>
    556 
    557 <li>A target of API level 19 or higher.</li>
    558 
    559 <li>A <code>&lt;provider&gt;</code> element that declares your custom storage
    560 provider. </li>
    561 
    562 <li>The name of your provider, which is its class name, including package name.
    563 For example: <code>com.example.android.storageprovider.MyCloudProvider</code>.</li>
    564 
    565 <li>The name of your authority, which is your package name (in this example,
    566 <code>com.example.android.storageprovider</code>) plus the type of content provider
    567 (<code>documents</code>). For example, {@code com.example.android.storageprovider.documents}.</li>
    568 
    569 <li>The attribute <code>android:exported</code> set to <code>&quot;true&quot;</code>.
    570 You must export your provider so that other apps can see it.</li>
    571 
    572 <li>The attribute <code>android:grantUriPermissions</code> set to
    573 <code>&quot;true&quot;</code>. This setting allows the system to grant other apps access
    574 to content in your provider. For a discussion of how to persist a grant for
    575 a particular document, see <a href="#permissions">Persist permissions</a>.</li>
    576 
    577 <li>The {@code MANAGE_DOCUMENTS} permission. By default a provider is available
    578 to everyone. Adding this permission restricts your provider to the system.
    579 This restriction is important for security.</li>
    580 
    581 <li>The {@code android:enabled} attribute set to a boolean value defined in a resource
    582 file. The purpose of this attribute is to disable the provider on devices running Android 4.3 or lower.
    583 For example, {@code android:enabled=&quot;&#64;bool/atLeastKitKat&quot;}. In
    584 addition to including this attribute in the manifest, you need to do the following:
    585 <ul>
    586 <li>In your {@code bool.xml} resources file under {@code res/values/}, add
    587 this line: <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;false&lt;/bool&gt;</pre></li>
    588 
    589 <li>In your {@code bool.xml} resources file under {@code res/values-v19/}, add
    590 this line: <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;true&lt;/bool&gt;</pre></li>
    591 </ul></li>
    592 
    593 <li>An intent filter that includes the
    594 {@code android.content.action.DOCUMENTS_PROVIDER} action, so that your provider
    595 appears in the picker when the system searches for providers.</li>
    596 
    597 </ul>
    598 <p>Here are excerpts from a sample manifest that includes a provider:</p>
    599 
    600 <pre>&lt;manifest... &gt;
    601     ...
    602     &lt;uses-sdk
    603         android:minSdkVersion=&quot;19&quot;
    604         android:targetSdkVersion=&quot;19&quot; /&gt;
    605         ....
    606         &lt;provider
    607             android:name=&quot;com.example.android.storageprovider.MyCloudProvider&quot;
    608             android:authorities=&quot;com.example.android.storageprovider.documents&quot;
    609             android:grantUriPermissions=&quot;true&quot;
    610             android:exported=&quot;true&quot;
    611             android:permission=&quot;android.permission.MANAGE_DOCUMENTS&quot;
    612             android:enabled=&quot;&#64;bool/atLeastKitKat&quot;&gt;
    613             &lt;intent-filter&gt;
    614                 &lt;action android:name=&quot;android.content.action.DOCUMENTS_PROVIDER&quot; /&gt;
    615             &lt;/intent-filter&gt;
    616         &lt;/provider&gt;
    617     &lt;/application&gt;
    618 
    619 &lt;/manifest&gt;</pre>
    620 
    621 <h4 id="43">Supporting devices running Android 4.3 and lower</h4>
    622 
    623 <p>The
    624 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} intent is only available
    625 on devices running Android 4.4 and higher.
    626 If you want your application to support {@link android.content.Intent#ACTION_GET_CONTENT}
    627 to accommodate devices that are running Android 4.3 and lower, you should
    628 disable the {@link android.content.Intent#ACTION_GET_CONTENT} intent filter in
    629 your manifest for devices running Android 4.4 or higher. A
    630 document provider and {@link android.content.Intent#ACTION_GET_CONTENT} should be considered
    631  mutually exclusive. If you support both of them simultaneously, your app will
    632 appear twice in the system picker UI, offering two different ways of accessing
    633 your stored data. This would be confusing for users.</p>
    634 
    635 <p>Here is the recommended way of disabling the
    636 {@link android.content.Intent#ACTION_GET_CONTENT} intent filter for devices
    637 running Android version 4.4 or higher:</p>
    638 
    639 <ol>
    640 <li>In your {@code bool.xml} resources file under {@code res/values/}, add
    641 this line: <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;true&lt;/bool&gt;</pre></li>
    642 
    643 <li>In your {@code bool.xml} resources file under {@code res/values-v19/}, add
    644 this line: <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;false&lt;/bool&gt;</pre></li>
    645 
    646 <li>Add an
    647 <a href="{@docRoot}guide/topics/manifest/activity-alias-element.html">activity
    648 alias</a> to disable the {@link android.content.Intent#ACTION_GET_CONTENT} intent
    649 filter for versions 4.4 (API level 19) and higher. For example:
    650 
    651 <pre>
    652 &lt;!-- This activity alias is added so that GET_CONTENT intent-filter
    653      can be disabled for builds on API level 19 and higher. --&gt;
    654 &lt;activity-alias android:name=&quot;com.android.example.app.MyPicker&quot;
    655         android:targetActivity=&quot;com.android.example.app.MyActivity&quot;
    656         ...
    657         android:enabled=&quot;@bool/atMostJellyBeanMR2&quot;&gt;
    658     &lt;intent-filter&gt;
    659         &lt;action android:name=&quot;android.intent.action.GET_CONTENT&quot; /&gt;
    660         &lt;category android:name=&quot;android.intent.category.OPENABLE&quot; /&gt;
    661         &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; /&gt;
    662         &lt;data android:mimeType=&quot;image/*&quot; /&gt;
    663         &lt;data android:mimeType=&quot;video/*&quot; /&gt;
    664     &lt;/intent-filter&gt;
    665 &lt;/activity-alias&gt;
    666 </pre>
    667 </li>
    668 </ol>
    669 <h3 id="contract">Contracts</h3>
    670 
    671 <p>Usually when you write a custom content provider, one of the tasks is
    672 implementing contract classes, as described in the
    673 <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContractClass">
    674 Content Providers</a> developers guide. A contract class is a {@code public final} class
    675 that contains constant definitions for the URIs, column names, MIME types, and
    676 other metadata that pertain to the provider. The SAF
    677 provides these contract classes for you, so you don't need to write your
    678 own:</p>
    679 
    680 <ul>
    681    <li>{@link android.provider.DocumentsContract.Document}</li>
    682    <li>{@link android.provider.DocumentsContract.Root}</li>
    683 </ul>
    684 
    685 <p>For example, here are the columns you might return in a cursor when
    686 your document provider is queried for documents or the root:</p>
    687 
    688 <pre>private static final String[] DEFAULT_ROOT_PROJECTION =
    689         new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
    690         Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
    691         Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
    692         Root.COLUMN_AVAILABLE_BYTES,};
    693 private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
    694         String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
    695         Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
    696         Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
    697 </pre>
    698 
    699 <h3 id="subclass">Subclass DocumentsProvider</h3>
    700 
    701 <p>The next step in writing a custom document provider is to subclass the
    702 abstract class {@link android.provider.DocumentsProvider}. At minimum, you need
    703 to implement the following methods:</p>
    704 
    705 <ul>
    706 <li>{@link android.provider.DocumentsProvider#queryRoots queryRoots()}</li>
    707 
    708 <li>{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}</li>
    709 
    710 <li>{@link android.provider.DocumentsProvider#queryDocument queryDocument()}</li>
    711 
    712 <li>{@link android.provider.DocumentsProvider#openDocument openDocument()}</li>
    713 </ul>
    714 
    715 <p>These are the only methods you are strictly required to implement, but there
    716 are many more you might want to. See {@link android.provider.DocumentsProvider}
    717 for details.</p>
    718 
    719 <h4 id="queryRoots">Implement queryRoots</h4>
    720 
    721 <p>Your implementation of {@link android.provider.DocumentsProvider#queryRoots
    722 queryRoots()} must return a {@link android.database.Cursor} pointing to all the
    723 root directories of your document providers, using columns defined in
    724 {@link android.provider.DocumentsContract.Root}.</p>
    725 
    726 <p>In the following snippet, the {@code projection} parameter represents the
    727 specific fields the caller wants to get back. The snippet creates a new cursor
    728 and adds one row to it&mdash;one root, a top level directory, like
    729 Downloads or Images.  Most providers only have one root. You might have more than one,
    730 for example, in the case of multiple user accounts. In that case, just add a
    731 second row to the cursor.</p>
    732 
    733 <pre>
    734 &#64;Override
    735 public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    736 
    737     // Create a cursor with either the requested fields, or the default
    738     // projection if "projection" is null.
    739     final MatrixCursor result =
    740             new MatrixCursor(resolveRootProjection(projection));
    741 
    742     // If user is not logged in, return an empty root cursor.  This removes our
    743     // provider from the list entirely.
    744     if (!isUserLoggedIn()) {
    745         return result;
    746     }
    747 
    748     // It's possible to have multiple roots (e.g. for multiple accounts in the
    749     // same app) -- just add multiple cursor rows.
    750     // Construct one row for a root called &quot;MyCloud&quot;.
    751     final MatrixCursor.RowBuilder row = result.newRow();
    752     row.add(Root.COLUMN_ROOT_ID, ROOT);
    753     row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
    754 
    755     // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
    756     // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
    757     // recently used documents will show up in the &quot;Recents&quot; category.
    758     // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
    759     // shares.
    760     row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
    761             Root.FLAG_SUPPORTS_RECENTS |
    762             Root.FLAG_SUPPORTS_SEARCH);
    763 
    764     // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
    765     row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
    766 
    767     // This document id cannot change once it's shared.
    768     row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
    769 
    770     // The child MIME types are used to filter the roots and only present to the
    771     //  user roots that contain the desired type somewhere in their file hierarchy.
    772     row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
    773     row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
    774     row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
    775 
    776     return result;
    777 }</pre>
    778 
    779 <h4 id="queryChildDocuments">Implement queryChildDocuments</h4>
    780 
    781 <p>Your implementation of
    782 {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
    783 must return a {@link android.database.Cursor} that points to all the files in
    784 the specified directory, using columns defined in
    785 {@link android.provider.DocumentsContract.Document}.</p>
    786 
    787 <p>This method gets called when you choose an application root in the picker UI.
    788 It gets the child documents of a directory under the root.  It can be called at any level in
    789 the file hierarchy, not just the root. This snippet
    790 makes a new cursor with the requested columns, then adds information about
    791 every immediate child in the parent directory to the cursor.
    792 A child can be an image, another directory&mdash;any file:</p>
    793 
    794 <pre>&#64;Override
    795 public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
    796                               String sortOrder) throws FileNotFoundException {
    797 
    798     final MatrixCursor result = new
    799             MatrixCursor(resolveDocumentProjection(projection));
    800     final File parent = getFileForDocId(parentDocumentId);
    801     for (File file : parent.listFiles()) {
    802         // Adds the file's display name, MIME type, size, and so on.
    803         includeFile(result, null, file);
    804     }
    805     return result;
    806 }
    807 </pre>
    808 
    809 <h4 id="queryDocument">Implement queryDocument</h4>
    810 
    811 <p>Your implementation of
    812 {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    813 must return a {@link android.database.Cursor} that points to the specified file,
    814 using columns defined in {@link android.provider.DocumentsContract.Document}.
    815 </p>
    816 
    817 <p>The {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    818 method returns the same information that was passed in
    819 {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()},
    820 but for a specific file:</p>
    821 
    822 
    823 <pre>&#64;Override
    824 public Cursor queryDocument(String documentId, String[] projection) throws
    825         FileNotFoundException {
    826 
    827     // Create a cursor with the requested projection, or the default projection.
    828     final MatrixCursor result = new
    829             MatrixCursor(resolveDocumentProjection(projection));
    830     includeFile(result, documentId, null);
    831     return result;
    832 }
    833 </pre>
    834 
    835 <h4 id="openDocument">Implement openDocument</h4>
    836 
    837 <p>You must implement {@link android.provider.DocumentsProvider#openDocument
    838 openDocument()} to return a {@link android.os.ParcelFileDescriptor} representing
    839 the specified file. Other apps can use the returned {@link android.os.ParcelFileDescriptor}
    840 to stream data. The system calls this method once the user selects a file
    841 and the client app requests access to it by calling
    842 {@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}.
    843 For example:</p>
    844 
    845 <pre>&#64;Override
    846 public ParcelFileDescriptor openDocument(final String documentId,
    847                                          final String mode,
    848                                          CancellationSignal signal) throws
    849         FileNotFoundException {
    850     Log.v(TAG, &quot;openDocument, mode: &quot; + mode);
    851     // It's OK to do network operations in this method to download the document,
    852     // as long as you periodically check the CancellationSignal. If you have an
    853     // extremely large file to transfer from the network, a better solution may
    854     // be pipes or sockets (see ParcelFileDescriptor for helper methods).
    855 
    856     final File file = getFileForDocId(documentId);
    857 
    858     final boolean isWrite = (mode.indexOf('w') != -1);
    859     if(isWrite) {
    860         // Attach a close listener if the document is opened in write mode.
    861         try {
    862             Handler handler = new Handler(getContext().getMainLooper());
    863             return ParcelFileDescriptor.open(file, accessMode, handler,
    864                         new ParcelFileDescriptor.OnCloseListener() {
    865                 &#64;Override
    866                 public void onClose(IOException e) {
    867 
    868                     // Update the file with the cloud server. The client is done
    869                     // writing.
    870                     Log.i(TAG, &quot;A file with id &quot; +
    871                     documentId + &quot; has been closed!
    872                     Time to &quot; +
    873                     &quot;update the server.&quot;);
    874                 }
    875 
    876             });
    877         } catch (IOException e) {
    878             throw new FileNotFoundException(&quot;Failed to open document with id &quot;
    879             + documentId + &quot; and mode &quot; + mode);
    880         }
    881     } else {
    882         return ParcelFileDescriptor.open(file, accessMode);
    883     }
    884 }
    885 </pre>
    886 
    887 <h3 id="security">Security</h3>
    888 
    889 <p>Suppose your document provider is a password-protected cloud storage service
    890 and you want to make sure that users are logged in before you start sharing their files.
    891 What should your app do if the user is not logged in?  The solution is to return
    892 zero roots in your implementation of {@link android.provider.DocumentsProvider#queryRoots
    893 queryRoots()}. That is, an empty root cursor:</p>
    894 
    895 <pre>
    896 public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    897 ...
    898     // If user is not logged in, return an empty root cursor.  This removes our
    899     // provider from the list entirely.
    900     if (!isUserLoggedIn()) {
    901         return result;
    902 }
    903 </pre>
    904 
    905 <p>The other step is to call {@code getContentResolver().notifyChange()}.
    906 Remember the {@link android.provider.DocumentsContract}?  Were using it to make
    907 this URI. The following snippet tells the system to query the roots of your
    908 document provider whenever the user's login status changes. If the user is not
    909 logged in, a call to {@link android.provider.DocumentsProvider#queryRoots queryRoots()} returns an
    910 empty cursor, as shown above. This ensures that a provider's documents are only
    911 available if the user is logged into the provider.</p>
    912 
    913 <pre>private void onLoginButtonClick() {
    914     loginOrLogout();
    915     getContentResolver().notifyChange(DocumentsContract
    916             .buildRootsUri(AUTHORITY), null);
    917 }
    918 </pre>