In my app the user can create a backup, with the following workflow:
user selects a destination (opening SAF UI and selecting a destination)
user chooses some options
user either presses a start button to create the backup, or cancels and goes back
I'm trying to do this with Storage Access Framework (since with Android 10 we are forced to use that... thing). I'm using an ACTION_CREATE_DOCUMENT intent. When doing so, Android creates an empty file as soon as the user picks a destination.
If the app crashes / is killed, or if the user goes back, the empty file will still be there (in case of the user going back of course I can delete it in onDestroy, but that will not work if there's a crash / kill).
To reduce the risk of having that empty file, I'd like to delete it in onActivityResult, and create it only if the user effectively creates the backup.
To do so, in onActivityResult I call takePersistableUriPermission, and then DocumentsContract.deleteDocument. But then when trying to create the file the app crashes:
Permission Denial: writing com.android.externalstorage.ExternalStorageProvider uri [...] from pid=9543, uid=10136 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
It works if I do not call DocumentsContract.deleteDocument, so my writing code seems correct (I'm using getContentResolver().openOutputStream).
So, the question is: is it crashing because calling DocumentsContract.deleteDocument revoked my write permission, even though I called takePersistableUriPermission??
If so, is there any way to delete the file while keeping the write permission? android.permission.MANAGE_DOCUMENTS is reserved to system apps, but is there a way with grantUriPermission()?
Even better, is there a way to prevent SAF to create a dummy empty file??
Related
I couldn't find any information about what happens when the user disables an app's permission while the app is running.
Is the application re-initilized?
I saw that in some apps if a Dialog or BottomSheet is open while I disable the permission, the dialog is no longer displayed when I return to the app.
Can anyone explain what happens in-detail when a permission is denied at runtime? Or does anyone have some useful links for me?
I would be especially interested in which lifecycle events are called when returning to the app.
When a previously granted permission is revoked through settings, the app is force stopped. You can see this by watching your app in the debugger. The app process is marked DEAD as soon as the permission is revoked.
Returning to the app will launch it from the main activity. I've never really looked into why this happens, but I assume it's because when a granted permission is revoked, the user could be deep into the app at a place where it is assumed the permission is granted. When the permission is revoked, there's no way to know if the screen they are currently in is even valid anymore.
Upon returning to the app, the app's state is restored and your current activity will be restarted, similar to a configuration change. If the activity you are in assumes a certain permission is granted, you should probably check that permission again in onCreate() to make sure you have it.
Simply put, That depends on what the app is trying to do when it needs permission.
For example: If we live in a country that requires you to be an adult to watch any video on YouTube, nothing will work with Location permissions denied
Another example: If you want to take photos using your phone via an app, the Camera permission should be permitted.
Under some circumstances, just part functions of app can not be used, but at an Extreme case, app would throw Security Exception and crash.
According to your point :
I saw that in some apps if a Dialog or BottomSheet is open while I
disable the permission, the dialog is no longer displayed when I
return to the app.
There is no lifecycle callback about what you do once permission is denied, but there's method on ActivityCompat which gives you flag if you want to show your own Dialog/BottomSheet
So, you can call shouldShowRequestPermissionRationale() method from ActivityCompat & make your own logic work when it's true.
shouldShowRequestPermissionRationale :
Gets whether you should show UI with rationale for requesting a permission. You should do this only if you do not have the permission and the context in which the permission is requested does not clearly communicate to the user what would be the benefit from granting this permission.
For example,
if you write a camera app, requesting the camera permission would be expected by the user and no rationale for why it is requested is needed.
If however, the app needs location for tagging photos then a non-tech savvy user may wonder how location is related to taking photos. In this case you may choose to show UI with rationale of requesting this permission.
While disabling permission for first time will give you callback in onRequestPermissionResult() method.
The PlacePicker is a useful widget added in com.google.android.gms:play-services-places:9.4.0. Find the description here: PlacePicker
The documentation clearly states that you need the ACCESS_FINE_LOCATION permission for it to work.
On Marshmallow and above you also must ask the user to grant this permission.
But it seems to work without doing any of this! My app does
provide a maps API key
not define the permission in the AndroidManifest
not ask the user to grant this permission at any time
But running the app on an Marshmallow device DOES start the PlacePicker and I can
correctly select a place (not possible without the API key).
go to "My Location" via the MyLocation-Button (possible without API key)
Can anyone confirm this or has an explanation why this widget works without proper permissions granted?
The question is almost an year old but I was trying to find the same answer so it can help somebody else.
It's because PlacePicker works with "intent based request". The documentation is not very clear with it, but it says
1: Only use the permissions necessary for your app to work. Depending on how you are using the permissions, there may be another way to do what you need (system intents, identifiers, backgrounding for phone calls) without relying on access to sensitive information.
Source
And give some clue here:
If your requirement for access to user data is infrequent — in other words, it's not unacceptably disruptive for the user to be presented with a runtime dialogue each time you need to access data — you can use an intent based request. Android provides some system intents that applications can use without requiring permissions because the user chooses what, if anything, to share with the app at the time the intent based request is issued.
Source
I've figured it out testing permissions use cases with Marshmallow and Lollipop devices, comparing the result with PlacePicker that I thought could be using intent based permissions with MediaStore that use this kind of permission according to documentation:
For example, an intent action type of MediaStore.ACTION_IMAGE_CAPTURE or MediaStore.ACTION_VIDEO_CAPTURE can be used to capture images or videos without directly using the Camera object (or requiring the permission). In this case, the system intent will ask for the user’s permission on your behalf every time an image is captured.
For example, an intent action type of MediaStore.ACTION_IMAGE_CAPTURE or MediaStore.ACTION_VIDEO_CAPTURE can be used to capture images or videos without directly using the Camera object (or requiring the permission). In this case, the system intent will ask for the user’s permission on your behalf every time an image is captured.
Source
I am curious about lifecycle of permission when using android.support.v4.content.FileProvider. Documentation for FileProvider says:
Set the android:grantUriPermissions attribute to true, to allow you to grant temporary access to files.
And
Permissions granted in an Intent remain in effect while the stack of the receiving Activity is active. When the stack finishes, the permissions are automatically removed. Permissions granted to one Activity in a client app are automatically extended to other components of that app.
But on the other side, documentation for providers says:
If you enable this feature, either by setting this attribute to "true" or by defining subelements, you must call Context.revokeUriPermission() when a covered URI is deleted from the provider.
Question is: What is "stack of receiving Activity"? Is is Back-stack(History)? If no, then when should I revoke permission (or at which time system will care about that)?
As I see this - stack it's a stack of activities inside application, which one use your file. It could be external activities, in case if you open file by 3rd party app, or your own activity, doesn't matter. You should revoke permissions as soon, as you don't need this file to be shared anymore.
I am upgrading my Android app's Target SDK Version to 23 (Android M) which has the new Runtime permissions (https://developer.android.com/training/permissions/requesting.html). My app has declared WRITE_EXTERNAL_STORAGE in its manifest, which must now me requested at runtime. Of course, I'd like to only request it when necessary.
I have an abstraction for different storage types, one implements local storage through normal File I/O (not the Android Content Provider stuff). In this class, I get a file path (like /mnt/sdcard/... or /data/data/...) and before accessing that file (read and write mode), I want to check if I have to call RequestPermissions for WRITE_EXTERNAL_STORAGE.
So the question is: What's the safest and simplest way to determine whether a file can be read and written without that permission (e.g. because it's inside getExternalFilesDir()) or not?
You can see here that you only have to ask for WRITE_EXTERNAL_STORAGE when your application needs to write to external storage.
But:
Starting in API level 19, this permission is not required to
read/write files in your application-specific directories returned by
getExternalFilesDir(String) and getExternalCacheDir().
First of all I recommend you to avoid requesting permissions using Intents, because it's a best practice and improves a lot the user experience.
If you can't use an Intent to avoid writing with your app, and you know that some day the user will have to write externally, I think the better would be to ask for the permissions the first time the user takes the "write in external storage" path.
As far as I know, in API23 you only have to ask for permissions once, so I think the easyest way would be to ask for the permissions at the first time that the user needs the functionality, I think that then when he'll execute it again, permissions would remain accepted. You can check your granted permissions with the procedure shown here.
Here you can read:
The user is prompted to give permission once, either at run time or at
install time (depending on the user's Android version). After that,
your app can perform the operation without requiring additional
interaction from the user. However, if the user doesn't grant the
permission (or revokes it later on), your app becomes unable to
perform the operation at all.
with Androids new permission system, I was wondering how to implement it right. The tutorials about how and when to use the permissions seem to be pretty clear. However, I don't know who requests the permissions and where to request them.
So, basically my question is: should the Activity, who starts another Activity request the permission beforehand or should the Activity which requires the permission place the request?
If the Activity which requires the permission should request for it, should I call requestForPermission inside onCreate or in onStart?
Though it seems to be very simple questions, I haven't found any hints in the documentation.
Thanks.
should the Activity, who starts another Activity request the permission beforehand or should the Activity which requires the permission place the request?
That is up to you. The main guidance is that there should be a clear tie from something the user does to your request for permissions:
If your app needs certain permissions to do anything meaningful, ask for them when your app starts up, perhaps after any sort of "welcome" presentation to advise them about why you need the permissions.
If your app needs certain permissions to do something based on the user performing some in-app action, like tapping on an action bar item or ListView row, ask for the permission when the user performs that action.
Asking for permissions at semi-random points in the app will simply lead to user confusion ("what did I do? why is it asking me this? and why are these questions appearing in an Stack Overflow answer?!?").
If your app can't function properly without a particular permission might be good to have a welcome permission flow where you explain why need the permissions and ask for the grants. For example : Google maps and location permission
If some specific parts of the app need a separate permission you can call the permission check just before doing a method call that needs permission. In this case you can create a wrapper for your function that needs contact permission and always call that wrapper instead of the actual method. For example : Google maps and microphone permission when you try to use the search with voice functionality
More details http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en
also check out https://github.com/permissions-dispatcher/PermissionsDispatcher could reduce a lot of permission code.
When ever your X task struck due to some "Y" permission then only ask for permission. There is no point of asking in onCreate or onStart method.
if you ask for "Y" permission at the start of Activity then there is no difference between Android M and below model. Exploit the beauty of Android M. for example if your require storage permission for creating a temp it's better make a temp file in App internal area i.e /data/data/your package name/files/ rather than asking for storage permission to users. Overall my point is exploit these options as much as you before it become necessary condition to ask for "Y" permission.
Regarding Activity concern , your task must be running be over some fragment or activity let that activity handle the onRequestPermission results.