Android storage access framework persistable permi

2019-09-07 07:45发布

问题:

I set the intent flags here:

public void createAlbum(View view) {
    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    String intentChooserDialog = getResources().getString(R.string.pick_image_dialog);
    startActivityForResult(Intent.createChooser(intent, intentChooserDialog), PICK_IMAGE);
}

Here in my activity result I set the persistable permissions as they are explained here:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE) {
        // Get image selection
        if (data != null) {
            ClipData clipData = data.getClipData();
            if (clipData != null) {
                int clipDataCount = clipData.getItemCount();
                if (clipDataCount > 1) {
                    int clipIndex = 0;
                    BitmapUri[] bitmapUris = new BitmapUri[clipDataCount];
                    while (clipIndex < clipDataCount) {
                        ClipData.Item item = clipData.getItemAt(clipIndex);
                        Uri uri = item.getUri();
                        int takeFlags = data.getFlags();
                        takeFlags &= Intent.FLAG_GRANT_READ_URI_PERMISSION;
                        getContentResolver().takePersistableUriPermission(uri, takeFlags);
                        BitmapUri bitmapUri = new BitmapUri(Uri.decode(uri.toString()),            data.getFlags());
                        bitmapUris[clipIndex] = bitmapUri;
                        clipIndex++;
                    }
                    DatabaseAlbumWriter dab = new DatabaseAlbumWriter(this);
                    dab.execute(bitmapUris);
                }
            } else {
                int oneImageSelected = 1;
                BitmapUri[] bitmapUris = new BitmapUri[oneImageSelected];
                Uri uri = data.getData();
                int takeFlags = data.getFlags();
                takeFlags &= Intent.FLAG_GRANT_READ_URI_PERMISSION;
                getContentResolver().takePersistableUriPermission(uri, takeFlags);
                BitmapUri bitmapUri = new BitmapUri(Uri.decode(uri.toString()), data.getFlags());
                bitmapUris[0] = bitmapUri;
                DatabaseAlbumWriter dab = new DatabaseAlbumWriter(this);
                dab.execute(bitmapUris[0]);
            }
        }
    }
}

After the content style Uri’s are saved to a sqlite3 database, I want to load them in a gridview, but asynchronously as described here. Here is my implementation of decodeSampledBitmapFromResource from the training guide. An object with a decoded String of the Uri and the persistable permission from the original intent are passed in. These values were saved in the database and loaded here again.

        protected Bitmap decodeSampledBitmapFromResource(BitmapUri bitmapUri, int reqWidth, int reqHeight) {
        Bitmap bitmap = null;
        Uri uri = Uri.parse(bitmapUri.getStringUri());
        int permissions = bitmapUri.getPermissions();
        permissions &= Intent.FLAG_GRANT_READ_URI_PERMISSION;
        try {
            getContentResolver().takePersistableUriPermission(uri, permissions);
            ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");
            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            Rect rect = new Rect(-1,-1,-1,-1);
            BitmapFactory.decodeFileDescriptor(fileDescriptor, rect, options);
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, rect, options);
            parcelFileDescriptor.close();
        } catch (NullPointerException | SecurityException | IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

It is here that I get this error:

05-08 14:17:40.850 2728-2813/com.example.app W/System.err: java.lang.SecurityException: No persistable permission grants found for UID 10058 and Uri 0 @ content://com.android.providers.media.documents/document/image:32
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.os.Parcel.readException(Parcel.java:1599)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.os.Parcel.readException(Parcel.java:1552)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.app.ActivityManagerProxy.takePersistableUriPermission(ActivityManagerNative.java:4217)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.content.ContentResolver.takePersistableUriPermission(ContentResolver.java:1703)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at com.example.app.AlbumCreator$BitmapWorkerTask.decodeSampledBitmapFromResource(AlbumCreator.java:395)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at com.example.app.AlbumCreator$BitmapWorkerTask.doInBackground(AlbumCreator.java:343)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at com.example.app.AlbumCreator$BitmapWorkerTask.doInBackground(AlbumCreator.java:329)

If I load the bitmap from a parcelFileDescriptor with BitmapFactory.decodeFileDescriptor in the activity result I do not get this error. So, from what I can tell, the AsyncTask is a different user with different privileges. Anyone know how to assign persistable permissions to an AsyncTask? I’d rather not load bitmaps on the UI thread.

Here is my manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".AlbumCreator"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!--
             ATTENTION: This was auto-generated to add Google Play services to your project for
             App Indexing.  See https://g.co/AppIndexing/AndroidStudio for more information.
        -->
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <activity
            android:name=".AlbumEditor"
            android:label="@string/title_activity_album_editor"
            android:parentActivityName=".AlbumCreator">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.app.AlbumCreator" />
        </activity>
    </application>
</manifest>

回答1:

I solved my own problem. If you look at my code you can see that I used intent.getData(). Then I persisted the permission with that uri. When I saved the uri for later use I decoded it. Uri.decode(uri.toString().

Encoded Uri: content://com.android.providers.media.documents/document/image%3A32 Decoded: content://com.android.providers.media.documents/document/image:32

So, I instead used intent.getDataString(), then used Uri.parse() to create a Uri for the takePersistableUriPermission() method. Then I saved the initial data string from the intent in the database which keeps the encoded components.



回答2:

You need to add the necessary permission in your Manifest. Please add :

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

This is necessary if you want to make write operations.

Give it a try and let us know if this helps.