Search code examples
androidusbstorage-access-frameworkremovable-storage

Browsing a removable USB OTG on Android N, is Storage Access Framework really the only way?


We have a custom android device that is running Nougat and is wrapped in a vendor specific launcher. I am trying to implement a File Manager for the said device with the use of API 23. This is because I've read that the only way to do it starting from Android M is with SAF.

The device I am developing on has 3 USB ports to which when a USB drive is connected the app would detect and transfer custom files from the USB to the device's Internal Storage (the device has no SD Card). Since direct access to mountable storage is a no-go on Android N I was thinking of doing this with SAF.

I have implemented broadcast receivers for USB Detach and Attach through the USB Manager and USB Host and can successfully detect when a USB is plugged in or not in a vain attempt to communicate with the device and try to extract the files. But I've also read that the Android USB Framework is only for sending small bytes for communicating with a USB connected device such as an Arduino board and what not.

I am now trying to implement a USB Document Provider to browse the USB. Since I cannot directly access the files according to this post.

I am following this source code to implement my USB Document Provider.

I'm currently implementing the queryRoots and queryChildDocuments part of the class. Most of the fields for the Cursor I understand what to put but the part for the COLUMN_DOCUMENT_ID stumbles me (for both methods).

Since the device is a removable USB I don't know what to put as a value for the column

Here is my queryRoots method implementation:

@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
    final MatrixCursor.RowBuilder row = result.newRow();
    row.add(Root.COLUMN_ROOT_ID, BuildConfig.DOCUMENTS_AUTHORITY);
    row.add(Root.COLUMN_ICON, R.drawable.ic_menu_manage);
    row.add(Root.COLUMN_TITLE, "Root Title");
    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_RECENTS);
    row.add(Root.COLUMN_SUMMARY, "USB Reader");
    row.add(Root.COLUMN_DOCUMENT_ID, "/");
    row.add(Root.COLUMN_MIME_TYPES, "*/*");

    Uri rootsUri = DocumentsContract.buildRootsUri(BuildConfig.DOCUMENTS_AUTHORITY);
    getContext().getContentResolver().notifyChange(rootsUri, null);
    return result;
}

Please correct me if I'm wrong but since the files I want to export are not your typical mime type files, I have set the COLUMN_MIME_TYPES to

*/*

And my queryChildDocuments method:

   @Override
    public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
                                      String sortOrder) throws FileNotFoundException {
        final MatrixCursor result = new
                MatrixCursor(resolveDocumentProjection(projection));
        String parentDocumenPath = getContext().getFilesDir().getPath() + "/" + parentDocumentId;
        File dir = new File(parentDocumenPath);
                    for (File file : dir.listFiles()) {
            String documentId = parentDocumentId + "/" + file.getName();
            includeFile(result, documentId);
        }

        return result;
    }

Apart from that, there is nothing else special to my implementation.

I then made an intent to open the documents provider through a navigation menu with this code:

if (id == R.id.nav_file_explorer) {
            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            startActivityForResult(intent, 1);
}

The custom document provider is shown on the list but the USB drive is not listed. Apart from that when I click on the document provider to open from my specified root it returns nothing. What am I doing wrong?


Solution

  • Accessing removable storage data can be done without SAF in Android M and up. You just need to do low-level-ish coding. As illustrated by this project. We in our project might opt for implementing something similar to this linked project since SAF is proving to be unreliable as of this time.

    The project also includes an implementation of SAF but with the low-level USB reading library as a base.