Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android 11 (R) file path access

Tags:

According to the docs file path access is granted in Android R:

Starting in Android 11, apps that have the READ_EXTERNAL_STORAGE permission can read a device's media files using direct file paths and native libraries. This new capability allows your app to work more smoothly with third-party media libraries.

The problem is that I can't get the file path from MediaStore, so how are we supposed to read a file path that we can't access/retrieve? Is there a way, I'm not aware of, that we can get the file path from MediaStore?


Furthermore, the docs say the following:

All Files Access

Some apps have a core use case that requires broad file access, such as file management or backup & restore operations. They can get All Files Access by doing the following:

  1. Declare the MANAGE_EXTERNAL_STORAGE permission.
  2. Direct users to a system settings page where they can enable the Allow access to manage all files option for your app.

This permission grants the following:

  • Read access and write access to all files within shared storage.
  • Access to the contents of the MediaStore.Files table.

But I do not need all file access, I only want the user to select a video from MediaStore and pass the file path to FFmpeg(it requires a file path). I know that I can no longer use the _data column to retrieve a file path.


Please note:

  • I know a Uri is returned from MediaStore and does not point to a file.
  • I know that I can copy the file to my application directory and pass that to FFmpeg, but I could do that before Android R.
  • I can not pass FileDescriptor to FFmpeg and I can not use /proc/self/fd/ (I get /proc/7828/fd/70: Permission denied when selecting a file from the SD Card), have a look at this issue.

So what am I supposed to do, am I missing something? What was meant with can read a device's media files using direct file paths and native libraries?

like image 498
HB. Avatar asked Feb 23 '20 08:02

HB.


People also ask

How do I give access to a file in Android 11?

On the Settings > Privacy > Permission manager > Files and media page, each app that has the permission is listed under Allowed for all files. If your app targets Android 11, keep in mind that this access to "all files" is read-only.

Why does Android 11 have restrictions file manager?

The reason while the android 11 OBB folder was restricted from being accessed was due to the fact that android wanted to stop files from interacting with each other, basically separating them in a container, while also stopping the user from manually making changes to files or app data.


2 Answers

After asking a question on issuetracker, I've come to the following conclusions:

  • On Android R, the File restrictions that were added in Android Q is removed. So we can once again access File objects.
  • If you are targeting Android 10 > and you want to access/use file paths, you will have to add/keep the following in your manifest:

    android:requestLegacyExternalStorage="true"
    

    This is to ensure that file paths are working on Android 10(Q). On Android R this attribute will be ignored.

  • Don't use DATA column for inserting or updating into Media Store, use DISPLAY_NAME and RELATIVE_PATH, here is an example:

    ContentValues valuesvideos;
    valuesvideos = new ContentValues();
    valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "YourFolder");
    valuesvideos.put(MediaStore.Video.Media.TITLE, "SomeName");
    valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, "SomeName");
    valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
    valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
    valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
    valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 1);
    ContentResolver resolver = getContentResolver();
    Uri collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
    Uri uriSavedVideo = resolver.insert(collection, valuesvideos);
    
  • You can no longer use the ACTION_OPEN_DOCUMENT_TREE or the ACTION_OPEN_DOCUMENT intent action to request that the user select individual files from Android/data/,Android/obb/and all sub-directories.

  • It is recommended to only use File objects when you need to perform "seeking", like when using FFmpeg, for example.
  • You can only use the data column to access files that are on the disk. You should handle I/O Exceptions accordingly.

If you want to access a File or want a file path from a Uri that was returned from MediaStore, I've created a library that handles all the exceptions you might get. This includes all files on the disk, internal and removable disk. When selecting a File from Dropbox, for example, the File will be copied to your applications directory where you have full access, the copied file path will then be returned.

like image 85
HB. Avatar answered Sep 23 '22 09:09

HB.


For getting path, i'm coping file with fileDescriptor to new path & i use that path.

Finding File Name:

private static String copyFileAndGetPath(Context context, Uri realUri, String id) {
    final String selection = "_id=?";
    final String[] selectionArgs = new String[]{id};
    String path = null;
    Cursor cursor = null;
    try {
        final String[] projection = {"_display_name"};
        cursor = context.getContentResolver().query(realUri, projection, selection, selectionArgs,
                null);
        cursor.moveToFirst();
        final String fileName = cursor.getString(cursor.getColumnIndexOrThrow("_display_name"));
        File file = new File(context.getCacheDir(), fileName);

        FileUtils.saveAnswerFileFromUri(realUri, file, context);
        path = file.getAbsolutePath();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return path;
}

Copy With File Descriptor:

fun saveAnswerFileFromUri(uri: Uri, destFile: File?, context: Context) {
    try {
        val pfd: ParcelFileDescriptor =
            context.contentResolver.openFileDescriptor(uri, "r")!!
        if (pfd != null) {
            val fd: FileDescriptor = pfd.getFileDescriptor()
            val fileInputStream: InputStream = FileInputStream(fd)
            val fileOutputStream: OutputStream = FileOutputStream(destFile)
            val buffer = ByteArray(1024)
            var length: Int
            while (fileInputStream.read(buffer).also { length = it } > 0) {
                fileOutputStream.write(buffer, 0, length)
            }

            fileOutputStream.flush()
            fileInputStream.close()
            fileOutputStream.close()
            pfd.close()
        }
    } catch (e: IOException) {
        Timber.w(e)
    }

}
like image 37
Hosein Haqiqian Avatar answered Sep 24 '22 09:09

Hosein Haqiqian