Content provider grant uri permission - android

I searched and read android developer files but I did not find any satisfactory answer for content provider grant uri permission. Can anybody explain more detailed and more simple. My questions are:
What grant uri is using for?
What is the differences between grant uri permission true and false
When we should use true? when false?
and any more detail are appreciated.

What grant uri is using for?
The "grant Uri permissions" feature allows you to have a ContentProvider that is normally inaccessible by third parties, yet selectively allow access to individual Uri values to individual third-party apps for a short period of time (e.g., long enough to view the PDF that the provider serves).
What is the differences between grant uri permission true and false
android:grantUriPermissions="true" indicates that your Java code can use FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION for any Uri served by that ContentProvider.
android:grantUriPermissions="false" indicates that only the Uri values specified by child <grant-uri-permission> elements can be used with FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION.

Say you need to mail some file that is on your application cache directory.
No other apps can access that file, unless you specify that other apps can access content of your application. For this you create content provider, and say, all uri's in form content://com.your.app/file you 'redirect' to your application cache directory.
Some code:
File f = ...; // Some local file.
Uri uri = Uri.parse("content://com.your.app/" + f.getName());
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Body");
intent.putExtra(Intent.EXTRA_STREAM, uri);
// You only can add flag FLAG_GRANT_READ_URI_PERMISSION if your app has
// android:grantUriPermissions="true" in manifest or see quote below.
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Send Email"));
As CommonsWare said:
android:grantUriPermissions="false" indicates that only the Uri values
specified by child <grant-uri-permission> elements can be used with
FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION.

with adapter class and In Kotlin :-
val uri: Uri? = FileProvider.getUriForFile(mContext, BuildConfig.APPLICATION_ID, file)
val sharingIntent = Intent(Intent.ACTION_SEND)
sharingIntent.type = "image/*"
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "Images ")
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri)
val chooser = Intent.createChooser(sharingIntent, "Share File")
val resInfoList: List<ResolveInfo> = mContext.packageManager
.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY)
for (resolveInfo in resInfoList) {
val packageName = resolveInfo.activityInfo.packageName
mContext.grantUriPermission(
packageName,
uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
mContext.startActivity(chooser)

Related

Wrong name of the shared file when choosing the app from Intent.createChooser

My app can share internally stored files with the androidx.core.content.FileProvider.
The intent and the chooser are created with the following snippet:
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(
Intent.EXTRA_STREAM,
FileProvider.getUriForFile(this, authority, file, displayName),
)
type = document.mimeType.mediaType
}
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_title)))
I explicitly pass the displayName to the FileProvider, but in the app chooser, I see the file's local name (see image).
Is there a way to show the correct display name in the chooser?

how to open files using intent action with latest Android storage framework?

Since Android introduced major changes in storage framework recently much of the documentation talks about permissions and scoped storage. But I couldn't find details on how to process Uri of a file, for it to be readable by other apps.
The intent action to view/read a file by other apps fail. I don't understand what's the problem here;
Does it have to do with difference between java.io.File and java.nio.File?
The Uri has missing permissions or the Uri is not well formatted.
The Android storage samples (FileManager) has this bug as well. It lists all the files in a directory successfully but can't open a selected image, or a document. I've reported this issue but no help so far.
Following snippet is from FileManager (storage-samples)
fun openFile(activity: AppCompatActivity, selectedItem: File) {
// Get URI and MIME type of file
val uri = Uri.fromFile(selectedItem).normalizeScheme()
val mime: String = getMimeType(uri.toString())
// Open file with user selected app
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.data = uri
intent.type = mime
return activity.startActivity(intent)
}
After the hints from the comments, I found the answer in developer docs.
Caution: If you want to set both the URI and MIME type, don't call setData() and setType() because they each nullify the value of the other. Always use setDataAndType() to set both URI and MIME type.
The reason behind openFile didn't throw FileUriExposedException in android-storage-samples is that after setting intent.type, the Uri gets nullified and when I changed it to setDataAndType() I got the exception. The final snippet looks like
fun openFile(activity: AppCompatActivity, selectedItem: File) {
// Get URI and MIME type of file
val uri = FileProvider.getUriForFile(activity.applicationContext, AUTHORITY, selectedItem)
// val uri = Uri.fromFile(selectedItem).normalizeScheme()
val mime: String = getMimeType(uri.toString())
// Open file with user selected app
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
// intent.data = uri
// intent.type = mime
intent.setDataAndType(uri, mime)
return activity.startActivity(intent)
}
I think they forgot to update the samples over time, let me create a pull request to commit this change over there as well.

Permission Denial while sharing file with FileProvider [duplicate]

This question already has answers here:
Permission Denial with File Provider through intent
(4 answers)
Closed 11 months ago.
I am trying to share file with FileProvider. I checked that file is shared properly with apps like Gmail, Google Drive etc. Even though following exception is thrown:
2019-08-28 11:43:03.169 12573-12595/com.example.name E/DatabaseUtils: Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content://com.example.name.provider/external_files/Android/data/com.example.name/files/allergy_report.pdf from pid=6005, uid=1000 requires the provider be exported, or grantUriPermission()
at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:729)
at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:602)
at android.content.ContentProvider$Transport.query(ContentProvider.java:231)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:104)
at android.os.Binder.execTransactInternal(Binder.java:1021)
at android.os.Binder.execTransact(Binder.java:994)
provider:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_provider_paths" />
</provider>
file_provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>
Sharing Intent
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(targetPdf);
if (fileWithinMyDir.exists()) {
intentShareFile.setType("application/pdf");
Uri uri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".provider", fileWithinMyDir);
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
intentShareFile.putExtra(Intent.EXTRA_SUBJECT, "Sharing File...");
intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
intentShareFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
Hopefully you can point out my mistake why this exceptions is thrown when it seems like apps are granted permission properly and sharing works as it should be.
EDIT:
I found that the problem lies in line:
startActivity(Intent.createChooser(intentShareFile, "Share File"));
When I changed it simply to
startActivity(intentShareFile);
However it displays a little bit different layout for picking application. But still I cannot figure out why original chooser is not working.
Sorry about the late response. I solved it like this:
Intent chooser = Intent.createChooser(intentShareFile, "Share File");
List<ResolveInfo> resInfoList = this.getPackageManager().queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
this.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(chooser);
This helped me: Permission Denial with File Provider through intent
There is also a way to grant the URI permission through the Intent flag, without doing in manually with grantUriPermission() and by keeping the usage of Intent.createChooser().
The Intent.createChooser() documentation states:
If the target intent has specified FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION, then these flags will also be set in the returned chooser intent, with its ClipData set appropriately: either a direct reflection of getClipData() if that is non-null, or a new ClipData built from getData().
Therefore, if the original Intent has the Uri set in its ClipData or in setData(), it should work as wanted.
In your example, the ACTION_SEND Intent supports the Uri set through setClipData() as of Jelly Bean (Android 4.1).
Long story short, here is a working example of your code:
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(targetPdf);
if(fileWithinMyDir.exists()) {
String mimeType = "application/pdf";
String[] mimeTypeArray = new String[] { mimeType };
intentShareFile.setType(mimeType);
Uri uri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".provider", fileWithinMyDir);
// Add the uri as a ClipData
intentShareFile.setClipData(new ClipData(
"A label describing your file to the user",
mimeTypeArray,
new ClipData.Item(uri)
));
// EXTRA_STREAM is kept for compatibility with old applications
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
"Sharing File...");
intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
intentShareFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
In addition to the answer of Nit above, it is possible to set the ClipData directly with the to be shared uri as follows.
intent.setClipData(ClipData.newRawUri("", uri));
This prevents the security exception when presenting the intent chooser.
If you want to share multiple uris, you can set the clipdata with multiple uris to prevent the security exception. To summarize:
Intent intent = new Intent();
intent.setType(mimeType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (uris.size() == 0) {
return;
}
else if (uris.size() == 1) {
Uri uri = uris.get(0);
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setClipData(ClipData.newRawUri("", uri));
}
else {
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
ClipData clipData = ClipData.newRawUri("", uris.get(0));
for (int i = 1; i < uris.size(); i++) {
Uri uri = uris.get(i);
clipData.addItem(new ClipData.Item(uri));
}
intent.setClipData(clipData);
}
startActivity(Intent.createChooser(intent, title));
This worked for me.
Intent sharableIntent = new Intent();
sharableIntent.setAction(Intent.ACTION_SEND);
sharableIntent.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION );
Uri imageUri = Uri.parse(ImgLoc);
File imageFile = new File(String.valueOf(imageUri));
Uri UriImage = FileProvider.getUriForFile(context, Author, imageFile);
sharableIntent.setType("image/*");
sharableIntent.putExtra(Intent.EXTRA_STREAM, UriImage);
sharableIntent.putExtra(Intent.EXTRA_TITLE, title);
sharableIntent.putExtra(Intent.EXTRA_TEXT, body);
Intent chooser = Intent.createChooser(sharableIntent, "Chooser Title");
chooser.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
context.startActivity(chooser);
createChooser
Intent.createChooser():Builds a new ACTION_CHOOSER Intent that wraps the given target intent, also optionally supplying a title. If the target intent has specified FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION, then these flags will also be set in the returned chooser intent, with its ClipData set appropriately: either a direct reflection of getClipData() if that is non-null, or a new ClipData built from getData()

Sharing video + text with FileProvider causes Permission Denial exception

i have Permission Denial while sharing a video .mp4 file with file provider and Intent.EXTRA_TEXT .
if i remove the line:
shareIntent.putExtra(Intent.EXTRA_TEXT, "#myappHashtag");
everything works. But i need both video file + extra text.
i think the problem is: caption=%23myappHashtag in the share uri but how can i fix it?
Thank you so much.
private void ShareItem(File fileToShare) {
String package_name = "com.lucagrillo.myapp";
Uri uri = FileProvider.getUriForFile(this, package_name + ".fileprovider", fileToShare);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.putExtra(Intent.EXTRA_TEXT, "#myappHashtag");
shareIntent.setType(intentFormatType);
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_with)));
}
Exception:
Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.lucagrillo.myapp.fileprovider/images/shareFile.mp4?caption=%23myappHashtag from pid=9630, uid=10093 requires the provider be exported, or grantUriPermission()
at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:608)
at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:483)
at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:474)
at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:419)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:313)
at android.os.Binder.execTransact(Binder.java:565)
Manifest file provider:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.lucagrillo.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
You missed the call to addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) on your Intent. That will work if your minSdkVersion is 21 or higher, to grant rights to that Uri to whatever app handles the ACTION_SEND request.
If your minSdkVersion is below 21, you're going to have to grant rights to all possible ACTION_SEND handlers for your Intent. On those older devices, addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) only grants permission to the Uri that you would have in the "data" facet of the Intent (e.g., new Intent(Intent.ACTION_VIEW, uri)), not Uri values in extras.
You wind up with code looking a bit like this:
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) {
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
else {
List<ResolveInfo> resInfoList=
getPackageManager()
.queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, outputUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
(where i is the Intent and outputUri is the Uri you put in the extra)
Also, as a reminder: ACTION_SEND implementations are not obligated to support both EXTRA_TEXT and EXTRA_STREAM.

Share multiple images from app internal memory

I have healthy working code to share single image from my app's internal memory using FileProvider facility given in android-support-v4.jar library.
Here is the code snippet
Uri uri = FileProvider.getUriForFile(MSafeGalleryActivity.this, "com.packagename.fileprovider", file);
final Intent intent = ShareCompat.IntentBuilder.from(SafeGalleryActivity.this)
.setType("image/jpeg")
.setStream(uri)
.setChooserTitle("Share with")
.createChooserIntent()
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
If you see the code it accept only one URI in setStream() method.
So Please can you help me out here to share multiple images from my app's internal memory.
To share multiple images, I use the following code:
ArrayList<Uri> imageUris = new ArrayList<>();
// Put your file uri in imageUris
// ...
// Create intent
Intent shareIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
// Grant permission to read files
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setType("image/*");
// Start chooser activity
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_send)))

Categories

Resources