My application works fine until I got the following error message recently:
java.lang.SecurityException: Unsupported path /storage/emulated/0/xxxzip/1234/abcde.zip
My permission in manifest file:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
And I also request run-time permission:
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this, new String[]{ android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
return true;
}
}
The app crashes at the last line of this code:
Uri Download_Uri = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(Download_Uri);
String downloadDir = savePath+"/zip/"+productId;
File direct = new File(downloadDir);
if(!direct.exists())
{
if(direct.mkdirs())
{
Log.d(TAG,"folder "+direct+" created");
}
}
int fileNamePos = url.lastIndexOf("/");
String fileName = url.substring(fileNamePos+1);
request.setDestinationUri(Uri.fromFile(new File(downloadDir + "/" + fileName)));
final long download_id = m_downloadManager.enqueue(request);
It was working previously and it's also working in simulator. Any idea?
By the way, here is the example of my download path: /storage/emulated/0/MyAppName/zip/1234. If I save to another folder like Downloads, it will be ok. So I think it's definitely the permission issue.
TLDR: Why isn't my app asking for runtime permissions despite I have written code for it?
I am saving an image into the storage, on button click by doing something like this:
// Function triggered when save button is pressed
private void saveImage(){
// Asking Runtime permissions for API level 23 and higher
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
finallySave();
} else {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},STORAGE_WRITE_REQUEST_CODE);
}
} else {
// Do not ask permissions in the runtime for API level 22 and lower
finallySave();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == STORAGE_WRITE_REQUEST_CODE) {
if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
finallySave()
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show()
}
}
}
private void finallySave(){
try {
File outFile = new File(imagesDir, "Abc " + System.currentTimeMillis() + ".png");
FileOutputStream outputStream = new FileOutputStream(outFile);
bitmaps[0].compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.flush();
outputStream.close();
scanFile(outFile, "image/png");
} catch (Exception unused) {
}
}
The problem is, sometimes the image is not saved and the following error is shown:
java.io.FileNotFoundException: /storage/emulated/0/Abc/Abc 1601370822977.png (Permission denied)
I found out that this error is thrown when the application is not asking "storage write" permissions on the runtime. Which is the very origin of the problem that I am facing. My app is not asking for runtime permissions in some cases despite I have written the code for it. Replicating this bug is also very difficult because many times the saving operation is carried out successfully.
I am sure that my code for saving images works fine because sometimes it works (this happens when my app asks for runtime permissions) and sometimes it doesn't.
P.S: I encountered this problem when I had reinstalled my app (after uninstalling).
EDIT: imagesDir is a global variable I am assigning is as follows:
imagesDir = getImageDir(); // in onCreate
private File getImageDir() {
File directory = new File(
Environment.getExternalStorageDirectory() + "/Abc");
if (!directory.exists()) {
if (!directory.mkdirs()) {
Toast.makeText(this, "Could not create directory", Toast.LENGTH_SHORT).show();
return null;
}
}
scanFile(directory, "*/*");
return directory;
}
I added the permissions to write access to the external storage device in the Manifest, and granted them in my Android phone. I even ask for them at execution time if they don't exist.
However, I always get this exception:
java.io.FileNotFoundException: /storage/emulated/03b3d97bd-5186-4506-97dc-9994b7ce0761 (Permission denied)
Do you have an idea of why?
Code
try {
if (Build.VERSION.SDK_INT >= 23) {
int permissionCheck = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
}
OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory() + image_uuid);
byte[] buffer = new byte[1024];
int bytesRead;
BitmapDrawable bitmapDrawable = (BitmapDrawable) temp.getDrawable();
etc. etc. etc.
And in the manifest file:
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
You write file in sd card before you get permission.You must wait for the result of granting permission by using below code:
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE/*1 in here*/: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
// write file here
} else {
// permission denied
}
return;
}
// other 'case' lines to check for other
// permissions this app might request.
}
}
see here
in the mainFest it has
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
and for newer OS it checks the permission
ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if it does not return true, it will request the permission
ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 5);
and only when it has the permission it will try to create the file
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String sysDownloadPath = directory.getPath();
try {
if (!directory.exists() && !directory.mkdirs()) {
//shouldn't happen in here
/* Checks if external storage is available for read and write */
String state = Environment.getExternalStorageState();
String externalStorageAvailable = (Environment.MEDIA_MOUNTED.equals(state)) ?
"ext-ST available" :
"ext-ST NOT available"; }
} catch (Exception e) { //<=== did not caught exception here
}
String fileName = ‘SD_20170404.pdf’;
String newFilePath = (sysDownloadPath + "/" + fileName);
File newFile = new File(newFilePath);
try {
newFile.createNewFile();// <=== throws at this one
} catch (Exception e) {
fatal Exception: java.lang.Exception: open failed: EACCES (Permission denied), newFile.getPath():/storage/emulated/0/Download/SD_20170404.pdf
It happens (not always but) with OS 5, 6 and 7. any suggestion? thanks!
I tried everything to write on external SD card on Android 6.0, but I am not able to write on it.
I did research on stackoverflow and found lot of things but none works. Here is my code
String extPath = System.getenv("SECONDARY_STORAGE") + "/Android/data/com.gvm.externalstorage.externalstoragetest/";
File file = new File(extPath,"myFiles");
if (!file.exists()) {
boolean dirResult = file.mkdirs();
Log.e("Directory Exist", dirResult + " Directory created");
} else {
Log.e("Directory Exist", "Exist");
Log.e("Direcotry Path",file.getAbsolutePath());
}
//String displayname = fileName.replace("%20", " ");
File outputFile = new File(file, "mytest5.txt");
outputFile.createNewFile();
This code works on Android 5.0 but not on Android 6.0.
Then I tried this path as well, and that gives me permission error, I have set all permission and managed code for runtime permission as well.
/mnt/media_rw/6AC9-083B
File write failed: java.io.IOException: open failed: EACCES (Permission denied)
If anyone can help me it would be great as I am trying this since last 3 days.
Thanks,
Anvesh
After long hard work I figured out a solution. In Android 6.0 it's not going to give you SD Card path always using this:
System.getenv("SECONDARY_STORAGE")
or this
Environment.getExternalStorageDirectory()
So I retrieved external SD Card path using this
File[] fs = context.getExternalFilesDirs(null);
String extPath = "";
// at index 0 you have the internal storage and at index 1 the real external...
if (fs != null && fs.length >= 2)
{
extPath = fs[1].getAbsolutePath();
Log.e("SD Path",fs[1].getAbsolutePath());
}
Rest everything will remain same for permission and all.
Thanks to those who helped me.
From API 23+(6.0) you need to request the read/write permissions even if they are already in your manifest known as Requesting Permissions at Run Time.
from docs
Beginning in Android 6.0 (API level 23), users grant permissions to
apps while the app is running, not when they install the app. This
approach streamlines the app install process, since the user does not
need to grant permissions when they install or update the app. It also
gives the user more control over the app's functionality; for example,
a user could choose to give a camera app access to the camera but not
to the device location. The user can revoke the permissions at any
time, by going to the app's Settings screen.
java
// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* #param activity
*/
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I think u should check your app permission first, to make sure your storage permission has been turned on.
If there's no storage permission:
Please check if u use this permission in your AndroidManifest
<uses-permission android:name="android.permission.STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
If the storage permission has been turned off:
Please check your runtime permission, maybe u can refer to this code
private void checkPermissions() {
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return;
}
final List<String> permissionsList = new ArrayList<String>();
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
permissionsList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
permissionsList.add(Manifest.permission.WRITE_CALENDAR);
permissionsList.add(Manifest.permission.READ_PHONE_STATE);
int permissionCheckLocation = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
int permissionCheckStorage = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
int permissionCheckCalendar = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.WRITE_CALENDAR);
int permissionCheckPhoneState = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.READ_PHONE_STATE);
boolean locationPermission=permissionCheckLocation == PackageManager.PERMISSION_GRANTED?true:false;
boolean storagePermission=permissionCheckStorage == PackageManager.PERMISSION_GRANTED?true:false;
boolean calendarPermission=permissionCheckCalendar == PackageManager.PERMISSION_GRANTED?true:false;
boolean phoneStatePermission=permissionCheckPhoneState == PackageManager.PERMISSION_GRANTED?true:false;
boolean shouldShowLocationPermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
boolean shouldShowStoragePermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
boolean shouldShowCalendarPermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.WRITE_CALENDAR);
boolean shouldShowPhoneStatePermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.READ_PHONE_STATE);
if (permissionCheckLocation == PackageManager.PERMISSION_GRANTED && permissionCheckStorage == PackageManager.PERMISSION_GRANTED
&& permissionCheckCalendar == PackageManager.PERMISSION_GRANTED && permissionCheckPhoneState == PackageManager.PERMISSION_GRANTED){
return;
}else if(((!locationPermission&&!shouldShowLocationPermission)||(!storagePermission&&!shouldShowStoragePermission)
||(!calendarPermission&&!shouldShowCalendarPermission)||(!phoneStatePermission&&!shouldShowPhoneStatePermission))&&appContext.localCheckPermission){
showMessageOKCancel("You need to allow access these permissions",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}
});
}else{
ActivityCompat.requestPermissions(IntroductionActivity.this, permissionsList.toArray(new String[permissionsList.size()]), 0);
}
}
If still have problem, please try to change your file path :
String fileName="mytest5.txt";
File folder = new File(Environment.getExternalStorageDirectory().getPath() + "/com.gvm.externalstorage.externalstoragetest/");
if (!folder.exists()) {
try {
folder.mkdirs();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Default Save Path Creation Error:" + folder);
}
}
File logFile = new File(folder, fileName);
if (!logFile.exists()) {
try {
logFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Default Save Path Creation Error:" + logFile);
}
}
Best regards. I hope this can help u
#Anvesh Another reliable method i'm using:
/**
* Get external storage path use reflect on android 6.0 device.
* Source code:
* https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/storage/StorageVolume.java
*
* #param removable the sdcard can remove or not, true means external sdcard, false means
* internal sdcard.
* #return path of sdcard we want
*/
public static String getStoragePath(boolean removable) {
WinZipApplication application = WinZipApplication.getInstance();
Context mContext = application.getApplicationContext();
StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = null;
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String path = (String) getPath.invoke(storageVolumeElement);
boolean mRemovable = (Boolean) isRemovable.invoke(storageVolumeElement);
if (removable == mRemovable) {
return path;
}
}
} catch (Exception e) {
return null;
}
return null;
}
After a lot of research I found ABSOLUTE SOLUTION. IT WORKS.
public boolean checkStorage() {
File[] fs = con.getExternalFilesDirs(null);
if (fs.length == 2)
return true;
else
return false;
}