3. @cketti
Why URI permissions?
Uri imageFileUri = Uri.fromFile(imageFile);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/png");
shareIntent.putExtra(Intent.EXTRA_STREAM, imageFileUri);
4. @cketti
Exposing file:// beyond your app is bad, m'kay?
Android 7.0 Behavior Changes:
“For apps targeting Android 7.0, the Android framework enforces the
StrictMode API policy that prohibits exposing file:// URIs outside your
app. If an intent containing a file URI leaves your app, the app fails with a
FileUriExposedException exception.”
5. @cketti
Why?
● You can’t assume receiving apps can access file:// URIs
● World-readable files in private storage have been deprecated for a while
● Cross-profile sharing doesn’t work with files
Read Mark Murphy’s blog post “PSA: file: Scheme Ban in N Developer Preview” to
learn more.
6. @cketti
URI permissions!
Uri imageProviderUri = …; // content://…
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/png");
shareIntent.putExtra(Intent.EXTRA_STREAM, imageProviderUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
10. @cketti
Use FileProvider (3)
Uri imageProviderUri = FileProvider.getUriForFile(
context,
"com.example.imageprovider",
file
);
Java:
11. @cketti
Manually granting URI permissions
● Context.grantUriPermission()
○ Package name (application ID)
○ URI
○ Flags
● Context.revokeUriPermission()
12. @cketti
Receiving Intent with URI permission grant
● Use ContentResolver.openInputStream()
● It just works™
● Almost
● Only temporary permission to access content
○ Lasts as long as the receiving component lives
○ Permission can be passed on to other components
13. @cketti
The tricky part
● FLAG_GRANT_*_URI_PERMISSION is available since first public Android
release
○ for the ‘data’ field in an Intent
● API 16 introduced ClipData on an Intent;
Flags apply to URIs in ClipData, too
● ACTION_SEND + EXTRA_STREAM?
○ The framework has you covered → Intent.migrateExtraStreamToClipData()
● ACTION_IMAGE_CAPTURE + ACTION_VIDEO_CAPTURE
○ API 21+
More information: “Granting Permissions on a Uri in an Intent Extra” - M. Murphy
14. @cketti
What if there’s no Intent?
Notification notif = new NotificationCompat.Builder(context)
.setSound(soundUri)
…
.build();
More information:
Mark Murphy - “Notifications, Sounds, Android 7.0, and Aggravation”
15. @cketti
What if there’s no Intent?
● Context.grantUriPermission()
○ Requires application ID of receiving app
● android.resource:// URI
● Custom ContentProvider
● StrictMode.setVmPolicy(…);
● targetSdkVersion < 24
18. @cketti
Audience feedback
● What could be a better title for this talk?
○ Death to file:// URIs
● Structure?
● More/better examples?
● More cat pictures?
19. @cketti
Summary
● Don’t use file:// URIs
● Use FileProvider
● Be aware of the permission grant lifetime
● Subscribe to Mark Murphy’s blog: https://commonsware.com/blog/