Android M User Permission Error - READ and WRITE - android

I just started off with android M and I am unable to access the external storage. I get the following error
Caused by: java.lang.SecurityException: Permission Denial: reading
com.android.providers.media.MediaProvider uri
content://media/external/images/media from pid=15355, uid=10053 requires
android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
I am adding user-permission in manifest like
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
and my build file is with following settings :
compileSdkVersion 23
buildToolsVersion "22.0.1"
minSdkVersion 16
targetSdkVersion 23
How to read and write from external storage in android M?

Reading from the documentation. I have to ask user for permission at runtime. Code example :
Add permission to android manifest like we used to do earlier :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Check if the user has already granted the permission. If yes, skip asking for permission and continue with your work flow else ask user for permission :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.System.canWrite(this)) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE}, 2909);
} else {
// continue with your code
}
} else {
// continue with your code
}
Now to check if the user granted the permission or denied it #Override OnRequestPermissionResult to get a callback :
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 2909: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("Permission", "Granted");
} else {
Log.e("Permission", "Denied");
}
return;
}
}
}
I was not able to READ external storage only by asking WRITE
permission, so i had to request for
Manifest.permission.READ_EXTERNAL_STORAGE as well.
Also if you want to target versions below api 23, check the Build
VERSION at runtime with IF statement and ask for permissions only if
VERSION is equal or above Android M.

In case you are looking for a way to handle in any android version (you are compiling for M, but some devices can be using other versions) there is a compatibility version.
if (ContextCompat.checkSelfPermission(CreatePlayerActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(CreatePlayerActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(CreatePlayerActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}

In regards to the selected answer:
Settings.System.canWrite(this) and WRITE_EXTERNAL_STORAGE are two very different things! Should be:
boolean needsRead = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED;
boolean needsWrite = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED;
if (needsRead || needsWrite)
requestStoragePermission()

I was struggling with WRITE_EXTERNAL_STORAGE permission: after I requested it, the dialog didn't show up and immediately returned DENIED.
The problem was in one of the included libraries. It contained the same permission declaration in its AndroidManifest.xml, but with maxSdkVersion="22". The declaration in application's manifest file was overridden by the library.
To override permission from the library I used the following:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:node="remove"
tools:selector="com.example.lib" />
I hope this might be helpful.

I used this utility on github. It works with with both API 22 and 23. Adding runtime permissions is not hard but having to seperate your code and move the methods around to capture callbacks is a little pain. This library provides a chained api to do all you need to do for supporting runtime permissions.
https://github.com/kayvannj/PermissionUtil

Related

Cannot request Bluetooth permission on Wear OS Samsung Galaxy Watch 4

I've created a sample Wear OS app, which should discover BLE devices, but my code requires Bluetooth permission. When I put these lines in manifest:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
corresponding permission is not displayed in settings/apps/permissions and every permission request does nothing. By the way, my BLE-devices (a speaker and a esp-32) is not shown in settings/Bluetooth also.
How can I grant Bluetooth permissions for my app or how can I connect BLE device to my watch?
upd:
I tried these:
if (checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH), 1001)
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH_CONNECT), 1001)
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH_SCAN), 1001)
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH_ADMIN), 1001)
}
But dialogs windows still are not displayed
According documentation, you need particular permissions in based of the target Android API version.
If your app targets Android 11 (API level 30) or lower, declare the following permissions in your app's manifest file:
BLUETOOTH is necessary to perform any Bluetooth classic or BLE communication, such as requesting a connection, accepting a connection, and transferring data.
ACCESS_FINE_LOCATION is necessary because, on Android 11 and lower, a Bluetooth scan could potentially be used to gather information about the location of the user.
If your app targets Android 9 (API level 28) or lower, you can declare the ACCESS_COARSE_LOCATION permission instead of the ACCESS_FINE_LOCATION permission.
In order to perform a scan to discover BLE devices the app must require explicitaly to the user the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission also to be declared in the AndroidManifest.xml.
In my project (WearOS API version 28) I used this code in the onCreate function of MainActivity class
if (ContextCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this,new String[] { Manifest.permission.ACCESS_FINE_LOCATION },
1);
}
And I overrided the onRequestPermissionsResult function
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Logger.d("MainActivity","Permission approved");
} else {
Logger.d("MainActivity","Error getting permission");
}
return;
}
}
This works for me, I hope would help you
there are some permissions like camera, bluetooth which need to be asked first and then manually provided. use this in the activity that loads first in your app.
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.CAMERA), 1001);
} //ask camera permissions
make sure to do required changes.

Is it possible to revoke permission with Espresso?

I'm currently writing some tests for an app. If the app does not have storage permission, a fragment pops up. I know how to grant permission with "grant permission rule" but is there a method to revoke permission?
No, that is not possible. Quote from https://developer.android.com/reference/android/support/test/rule/GrantPermissionRule:
Once a permission is granted it will apply for all tests running in the current Instrumentation. There is no way of revoking a permission after it was granted. Attempting to do so will crash the Instrumentation process.
What I can suggest is to wrap permission methods into some service and mock the value checking if the permission is granted
Yes, during testing its possible...Feuby gave a somewhat correct answer to the same question in this SO 2017 question
Android revoke permission at start of each test
Repeating his code
public static boolean checkCameraPermission(MainActivity thisActivity) {
return ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
public static void checkAndAskCameraPermission(final MainActivity thisActivity) {
if (!checkCameraPermission(thisActivity)) {
//No right is granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.CAMERA)) {
//Open a dialog explaining why you are asking permission then when when positive button is triggered, call this line
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.CAMERA},
CHECK_FOR_CAMERA_PERMISSION);
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.CAMERA},
CHECK_FOR_CAMERA_PERMISSION);
}
}
}

Oreo (WRITE EXTERNAL STORAGE) Permission

According to "developer.android.com"
If the app targets Android 8.0 (API level 26), the system grants only
READ_EXTERNAL_STORAGE at that time; however, if the app later requests
WRITE_EXTERNAL_STORAGE, the system immediately grants that privilege
without prompting the user.
Now, I have the READ_EXTERNAL_STORAGE and I'm requesting the DownloadManager to download a file in the Public Download folder
downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mAttachmentItem.getFilename());
Unfortunately, I got
E/UncaughtException: java.lang.IllegalStateException: Unable to create directory: /storage/emulated/0/data/user/0/com.abc.cba/files
at android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.java:699)
I have declared both of permissions in the Manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Read permission is already Granted and in Runtime I'm requesting Write permission, the system doesn't prompt any message or dialog, it's always Denied(Not Granted) in "onRequestPermissionsResult"
public static boolean isPermissionsToAccessStorageAreGranted(Context context, Activity activity) {
ArrayList<String> permissions = new ArrayList<>();
if (!PermissionUtil.checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if (!permissions.isEmpty()) {
String[] permissionsList = permissions.toArray(new String[permissions.size()]);
PermissionUtil.requestPermissions(activity, permissionsList,
PermissionUtil.REQUESTCODE_ACCESS_STORAGE);
return false;
} else {
return true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==REQUESTCODE_ACCESS_STORAGE && grantResults[0]== PackageManager.PERMISSION_GRANTED){
/* downloadFile(); */
}
}
I'm trying to grant those two permissions via ADB, it shows me an error:
adb shell pm grant com.abc.cba
android.permission.WRITE_EXTERNAL_STORAGE
Operation not allowed: java.lang.SecurityException: Package com.abc.cbs
has not requested permission android.permission.WRITE_EXTERNAL_STORAGE
adb shell pm grant com.abc.cba
android.permission.READ_EXTERNAL_STORAGE
Operation not allowed: java.lang.SecurityException: Can't change
android.permission.READ_EXTERNAL_STORAGE. It is required by the
application
Actually, It was an external problem. One of App Libs I'm using request the WRITE_EXTERNAL_PERMISSION with android:maxSdkVersion. So when merging with my Manifest it will remove the permission for the whole application.
The Solution is to add:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>
You are correct in adding the permissions to the manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
For versions of Lollipop and higher, you need to also request the permissions at runtime. To solve this problem, I created a new method requestAppPermission that I call when the main activity is created. This method runs only for Lollipop and higher, and returns early otherwise:
private void requestAppPermissions() {
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return;
}
if (hasReadPermissions() && hasWritePermissions()) {
return;
}
ActivityCompat.requestPermissions(this,
new String[] {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, REQUEST_WRITE_STORAGE_REQUEST_CODE); // your request code
}
private boolean hasReadPermissions() {
return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}
private boolean hasWritePermissions() {
return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}
I call this method in the activity's onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other things
requestAppPermissions();
}
On first load, this will prompt the user to accept permissions.
Then, before you run any code that needs to read and write to storage, you can check these permissions, either by storing the values or running those checks again using methods hasReadPermissions() and hasWritePermissions() defined above.
UPDATE: See this answer for a better solution
EDIT: This is not a solution, just a quick workaround.
If you get permission denied error even when the permissions are granted and you already implemented permission checks,
make sure you're not targetting api level 29:
Change targetSdkVersion and compilesdkversion from 29 to 28 or any other lower level.
For Oreo,
you need to explicitly do a READ_EXTERNAL_STORAGE request (by code) even if you have already requested and is granted the WRITE_EXTERNAL_STORAGE permission, and vis versa.
prior to this, READ_EXTERNAL_STORAGE is automatically granted when WRITE_EXTERNAL_STORAGE is granted.
so what you need to do, is to
1) ask for WRITE permission in your codes.
2) when user grants the WRITE permission, ask again for READ permission - this will be automatically granted (you will get an exception if you do not ask for a READ explicitly)
The changes for Oreo doesn't make much sense to me (no idea why do we need to ask for a permission that is automatically granted), but that is what it is.
https://developer.android.com/about/versions/oreo/android-8.0-changes.html#rmp
You have to grant permissions at runtime on Marshmallow or higher.
Sample snippet :
private static final int REQUEST_WRITE_STORAGE = 112;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean hasPermission = (ContextCompat.checkSelfPermission(getBaseContext(),
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
if (!hasPermission) {
ActivityCompat.requestPermissions(SplashScreen.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.INTERNET
},
REQUEST_WRITE_STORAGE);
}else{ startMainActivity(); }
}
Hope it helps.
Use this
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/>
instead of
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE/>"
Some of your third party libraries are override maxSdkVersion like com.vungle:publisher-sdk-android, to finding them just check the Merged Manifest below of your manifest screen. see this
For SDK 29 and Later
If you are still having issue to accessing external storage, consider using android:requestLegacyExternalStorage="true" in your <application> tag of your manifest.
Just a notice: on Android P I had to show a terminate dialog box to user asking to restart the app because even after all written above app couldn't read external storage, only after restart. Seriously, those guys in google makes Android better each time. 6, 8, and now so much troubles with 9, one more such stupid update and I will go to iOS :)

ask android permission on android 4?

I have an app that I published, and it has something like this:
requestStoragePermission();
and the function:
private void requestStoragePermission(){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},STORAGE_PERMISSION_CODE);
}
#Override //user response
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if(requestCode == STORAGE_PERMISSION_CODE){
//If permission is granted
if(grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//Displaying a toast
String string = getString(R.string.permissionOk);
Toast.makeText(this,string,Toast.LENGTH_LONG).show();
}
else{
//Displaying another toast if permission is not granted
String string = getString(R.string.permissionError);
Toast.makeText(this,string,Toast.LENGTH_LONG).show();
}
}
}
It works ok on android 6... but how will it be interpretation on android 4, for example, which does not need permissions? is it a bad practise?
On API levels where it is not required for the user to grant a dangerous permission, requesting that permission does not pop up a dialog for the user and the permission is automatically granted. This still adheres with Google's security policy because for those API levels the user has to grant the permissions during the installation process instead.
Some will tell you to check the API level in your code and only request the permission if it is below 23. This is just an extra layer on top of what the code already does internally and thus increases the complexity of your code. You don't have to do that.
There is no way to request persmission in android 4.
You simply have to add the permissions in android-manifest file and everything will be taken care of.
However, make sure your request permission code is not executed in these versions. For that use something like:
if(Build.VERSION.SDK_INT<23){
//DoNOtRequestPersmissions
}
In my projects, to request permissions, I usually have a function that I call arePermissionsGranted(). In that function, if the API level is lower than 23, it automatically says 'the permissions are granted', since you do not need to request them. If API level is 23 or higher, it checks if the permissions have been granted and if not, requests them.

Android permission automatically denied on requestPermission from MainActivity

In the main activity of an Android application I check for permissions (Manifest.permission.MANAGE_DOCUMENTS), detect I do not have them, and call requestPermisions.
Then in onRequestPermissionResult I almost immediately get denied permission, without a dialog ever showing.
I already confirmed the same permission in another activity of the same app (through requestPermissions again, which works), so I expected this decision to be stored (for the session, or whatever), and I have never selected to deny the permission. Either way, the permission dialog is not displayed and the permission is denied automatically.
So far I have tested this on emulators of Android 7 and 6 (API 24 and 23).
I have tried:
clearing the app's data cache and wiping the device, so it's fresh
definitely not this Getting permission denied on Android M
triple-checked spelling after seeing this onRequestPermissionsResult returns immediately with denied permission and I am calling the super method
a bunch of other suggestions on stack overflow
I'm pretty stumped...
Here is the permission request (see comment in the code):
private fun askForPermissionOrSendRequest(view: View, permission: String) {
if (checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
if (shouldShowRequestPermissionRationale(permission)) {
cachedView = view
val explanationDialog = AlertDialog.Builder(this).setMessage("We need permissions to read your storage in order to show your profile image.").setOnDismissListener {
requestPermissions(
arrayOf(permission),
BSMainActivity.permissionRequestSendProfilePic
)
}.create()
explanationDialog.show()
} else {
cachedView = view
// this branch is always hit - the permission seems to be missing every time
requestPermissions(
arrayOf(permission),
BSMainActivity.permissionRequestSendProfilePic
)
}
} else {
sendRequest(view)
}
}
I immediately get to the result handler without a dialog showing up to ask me for permissions. I may or may not have confirmed the same permission in another (child) activity of the same app (doesn't seem to make a difference).
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
BSMainActivity.permissionRequestSendProfilePic -> {
// This gets hit, MANAGE_DOCUMENTS was denied
if (permissions.contains(Manifest.permission.MANAGE_DOCUMENTS) && grantResults[permissions.indexOf(Manifest.permission.MANAGE_DOCUMENTS)] == PackageManager.PERMISSION_DENIED) {
Log.w(logName, "Permission to open image was denied while sending a tag request: %s %s".format(
permissions.joinToString(",", "[", "]"),
grantResults.joinToString(",", "[", "]")
))
}
// send request regardless of the result for now
sendRequest(cachedView)
}
}
}
In my manifest I have the following:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.monomon.bs">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
Only dangerous permissions can be requested at runtime and MANAGE_DOCUMENTS is not a dangerous permission.
As per the MANAGE_DOCUMENTS documentation:
This permission should only be requested by the platform document management app. This permission cannot be granted to third-party apps.

Categories

Resources