Issues traversing through directory hierarchy with Android Storage Access Framework / DocumentProvider using MTP
<p>UPDATE:
My initial question may be misleading so I want to rephrase it:
I want to traverse through the hierarchy tree from an MTP connected device through Android's Storage Access Framework.  I can't seem to achieve this because I get a <code>SecurityException</code> stating that a subnode is not a descendant of its parent node.  Is there a way to workaround this issue?  Or is this a known issue?  Thanks.</p>

<p>I'm writing an Android application that attempts to traverse and access documents through the hierarchy tree using Android's Storage Access Framework (SAF) via the <code>MtpDocumentsProvider</code>. I am more or less following the code example described in <a href="https://github.com/googlesamples/android-DirectorySelection" rel="noreferrer">https://github.com/googlesamples/android-DirectorySelection</a> on how to launch the SAF Picker from my app, select the MTP data source, and then, in <code>onActivityResult</code>, use the returned <code>Uri</code> to traverse through the hierarchy.  Unfortunately, this doesn't seem to work because as soon as I access a sub-folder and try to traverse that, I always get a <code>SecurityException</code> stating that <code>document xx is not a descendant of yy</code></p>

<p>So my question is, using the <code>MtpDocumentProvider</code>, how can I successfully traverse through the hierarchy tree from my app and avoid this exception?</p>

<p>To be specific, in my app, first, I call the following method to launch the SAF Picker:</p>

<pre><code>private void launchStoragePicker() {
    Intent browseIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    browseIntent.addFlags(
        Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
            | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
            | Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
    );
    startActivityForResult(browseIntent, REQUEST_CODE_OPEN_DIRECTORY);
}
</code></pre>

<p>The Android SAF picker then launches, and I see my connected device recognized as the MTP data source.  I select said data source and I get the <code>Uri</code> from my <code>onActivityResult</code>:</p>

<pre><code>@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_OPEN_DIRECTORY &amp;&amp; resultCode == Activity.RESULT_OK) {
        traverseDirectoryEntries(data.getData()); // getData() returns the root uri node
    }
}
</code></pre>

<p>Then, using the returned <code>Uri</code>, I call <code>DocumentsContract.buildChildDocumentsUriUsingTree</code> to get a <code>Uri</code> which I can then use to query and access the tree hierarchy:</p>

<pre><code>void traverseDirectoryEntries(Uri rootUri) {
    ContentResolver contentResolver = getActivity().getContentResolver();
    Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, DocumentsContract.getTreeDocumentId(rootUri));

    // Keep track of our directory hierarchy
    List&lt;Uri&gt; dirNodes = new LinkedList&lt;&gt;();
    dirNodes.add(childrenUri);

    while(!dirNodes.isEmpty()) {
        childrenUri = dirNodes.remove(0); // get the item from top
        Log.d(TAG, "node uri: ", childrenUri);
        Cursor c = contentResolver.query(childrenUri, new String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
        try {
            while (c.moveToNext()) {
                final String docId = c.getString(0);
                final String name = c.getString(1);
                final String mime = c.getString(2);
                Log.d(TAG, "docId: " + id + ", name: " + name + ", mime: " + mime);
                if(isDirectory(mime)) {
                    final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
                    dirNodes.add(newNode);
                }
            }
        } finally {
            closeQuietly(c);
        }
    }
}

// Util method to check if the mime type is a directory
private static boolean isDirectory(String mimeType) {
    return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}

// Util method to close a closeable
private static void closeQuietly(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (RuntimeException re) {
            throw re;
        } catch (Exception ignore) {
            // ignore exception
        }
    }
}
</code></pre>

<p>The first iteration on the outer while loop succeeds: the call to <code>query</code> returned a valid <code>Cursor</code> for me to traverse.  The problem is the second iteration: when I try to query for the <code>Uri</code>, which happens to be a subnode of <code>rootUri</code>, I get a <code>SecurityException</code> stating the document xx is not a descendent of yy.</p>

<blockquote>
  <p>D/MyApp(19241): node uri: content://com.android.mtp.documents/tree/2/document/2/children
  D/MyApp(19241): docId: 4, name: DCIM, mime: vnd.android.document/directory
  D/MyApp(19241): node uri: content://com.android.mtp.documents/tree/2/document/4/children
  E/DatabaseUtils(20944): Writing exception to parcel
  E/DatabaseUtils(20944): java.lang.SecurityException: Document 4 is not a descendant of 2</p>
</blockquote>

<p>Can anyone provide some insight as to what I'm doing wrong? If I use a different data source provider, for example, one that is from external storage (i.e. an SD Card attached via a standard USB OTG reader), everything works fine.</p>

<p>Additional information:
I'm running this on a Nexus 6P, Android 7.1.1, and my app <code>minSdkVersion</code> is 19.</p>