I have an application that correctly works with files in "external storage".
Recently I upgrade Android Studio from 2.2 to 2.3. And after this upgrade an application fails when creating files in external storage with EACCESS (Permission denied).
Affected versions
I have error in Android 4.0.3, 4.1, 4.2
I do not have error in Android 4.3, 4.4 and higher.
Code example
It is part of code, that fails
File tempFile = new File(Environment.getExternalStorageDirectory().toString() + "/.tmpfile");
if (!tempFile.exists() && !tempFile.createNewFile()) {
throw new IOException("Cannot create temp file");
}
And in method createNewFile() throws exception with EACCES:
java.io.IOException: open failed: EACCES (Permission denied)
at java.io.File.createNewFile(File.java:948)
...
Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
at libcore.io.Posix.open(Native Method)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
at java.io.File.createNewFile(File.java:941)
...
java.io.IOException: open failed: EACCES (Permission denied)
at java.io.File.createNewFile(File.java:948)
...
Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
at libcore.io.Posix.open(Native Method)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
at java.io.File.createNewFile(File.java:941)
... 15 more
Of course, I have <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> in AndroidManifest.xml.
Application have granted permissions. I check it in main activity via:
if (checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_CODE);
}
and have requestCode == PackageManager.PERMISSION_GRANTED is true.
The most strange: when I try to rollback my code to previous stable production releases (when files was succesfully created in 2.2) and create build, I get the same error! :( I get this error in emulator, in real devices.
Why? What was changed in Android Studio 2.3, that I cant create any file in old Android?
UPD
Thanks for Nick, he helps me to find difference in behaviour. I test below lines on Android 4.2 and 4.3, and get:
Environment.getExternalStorageDirectory()
4.2: '/mnt/sdcard'
4.3: '/storage/sdcard'
ContextCompat.getExternalFilesDirs(this, null)
4.2: {null} (array with one null-element)
4.3: '/storage/sdcard/Android/data/com.example/files'
Environment.getExternalStorageState()
4.2: 'removed'
4.3: 'mounted'
Your problem concerns changes made in Android 4.4. To quote documentation:
Sometimes, a device [...] may also offer an SD card slot. When such a device is running Android 4.3 and lower, the getExternalFilesDir() method provides access to only the internal partition and your app cannot read or write to the SD card
The documentation also offers a solution:
If you'd like to access both possible locations while also supporting Android 4.3 and lower, use the support library's static method
This refers to using ContextCompat rather than the core Android method
File[] dirs = ContextCompat.getExternalFilesDirs(ctx, null); //null, no specific sub directory
if (dirs.length > 0) {
File ext = dirs[dirs.length -1]; //Just presuming SD card will be the last one offered
//use ext here like you used tempFile before
}
You should also make sure that the media is available, as lower Android versions are more likely to be using low performance devices. Do that with the Environment class
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
//safe
}
EDIT
So a complete solution might be to replace
File tempFile = new File(Environment.getExternalStorageDirectory().toString() + "/.tmpfile");
With
File tempFile = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
//compatible for ALL the versions
File[] dirs = ContextCompat.getExternalFilesDirs(ctx, null); //null, no specific sub directory
if (dirs.length > 0) {
tempFile = dirs[dirs.length -1];
}
}
if (tempFile != null) {
//here continue exactly as you did before
if (!tempFile.exists() && !tempFile.createNewFile()) {
throw new IOException("Cannot create temp file");
}
} else {
//handle case where sd card isnt reachable (notification etc)
}
Related
In the app, I have implemented download manager to download new app version (APK file) and then install it. I've recently migrated to scoped storage in order to use app's private external directory. This is the code:
private void setupDownloadRequest() {
mDownloadRequest = new DownloadManager.Request(Uri.parse(mDownloadUrl));
mDownloadRequest.setTitle(mFileName);
mDownloadRequest
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
mDownloadRequest.setMimeType(getMimeType());
mDownloadRequest.setDestinationInExternalFilesDir(mActivity, Environment.DIRECTORY_DOWNLOADS, mFileName);
}
The process works well on Android <10 and also on emulator with Android 10. After release, I'm seeing the crash only on Huawei Mate 9 and Xiaomi MI8 (Android 9 and 10):
Fatal Exception: java.lang.IllegalStateException: Failed to get external storage files directory
at android.app.DownloadManager$Request.setDestinationInExternalFilesDir(DownloadManager.java:782)
...
Looking at the code in Android SDK (API 29), directory seems to be missing:
public Request setDestinationInExternalFilesDir(Context context, String dirType,
String subPath) {
final File file = context.getExternalFilesDir(dirType);
if (file == null) {
throw new IllegalStateException("Failed to get external storage files directory");
...
Do you have any idea how to fix this? Is Environment.DIRECTORY_DOWNLOADS directory missing on some devices, shouldn't it be created automatically?
Before download, I check that the permission Manifest.permission.WRITE_EXTERNAL_STORAGE is granted.
Add android:requestLegacyExternalStorage="true" in your manifest Application tag for Android 10 Devices
I have this in my manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
This is where I am trying to create and write to a file (it is in a file called EnterUserInfo.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) {
System.out.println("INSIDEEEEEE");
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
} else {
System.out.println("HEREEEEEEEEE");
}
}
private void writeToFile(String data, Context context) {
verifyStoragePermissions(this);
String FILENAME = "new_clients.txt";
String string = "hello world!";
try {
FileOutputStream fos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileOutputStream fos = context.openFileOutput("new_clients.txt", Context.MODE_PRIVATE);
fos.write(data.getBytes());
fos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
When I try to create a file, this is what appears:
I/System.out: HEREEEEEEEEE
W/ContextImpl: Failed to ensure /data/user/0/c.b.project/files: mkdir failed: EACCES (Permission denied)
W/FileUtils: Failed to chmod(/data/user/0/cs.b07.cscb07courseproject/files): android.system.ErrnoException: chmod failed: EACCES (Permission denied)
W/System.err: java.io.FileNotFoundException: /data/user/0/c.b.project/files/new_clients.txt (Permission denied)
W/System.err: at java.io.FileOutputStream.open(Native Method)
W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
W/System.err: at android.app.ContextImpl.openFileOutput(ContextImpl.java:506)
W/System.err: at android.content.ContextWrapper.openFileOutput(ContextWrapper.java:192)
W/System.err: at EnterUserInfo.writeToFile(EnterUserInfo.java:69)
As you can see, it prints here meaning the permission is granted, but right after it gives a Permission Denied error. Any idea how to solve this?
Edit: On a side note, when it says that it tries to save to /data/user/0/cs.b07.cscb07courseproject/files, is that within the project or is that saved on my computer? Because when I go to my terminal and do cd /data/ or cd /data neither is found.
Edit: writeToFile() is called in the same class and file posted above, and this is the code (the function below is called when a user hits the "register" button in the UI:
public void createNewUser(View view) {
// a data string is created here:
// String data = "asd";
writeToFile(data, this);
}
Edit 2: Please note that I did ask for permission at runtime in my verifyStoragePermissions() method. Unless something is wrong with that way of asking for permission (which I don't think it is because a prompt does appear which asks the user for permission), then I think the issue is with something else.
You do not need any permissions to call openFileOutput(). This writes a file to the private application-specific data area, which is owned by your application.
Judging by these errors:
W/ContextImpl: Failed to ensure /data/user/0/c.b.project/files: mkdir failed: EACCES (Permission denied)
W/FileUtils: Failed to chmod(/data/user/0/cs.b07.cscb07courseproject/files): android.system.ErrnoException: chmod failed: EACCES (Permission denied)
It looks like someone has changed the file ownership (or access rights) on your application's private data directory /data/user/0/c.b.project/. This directory should be owned by your application's user ID and therefore your application should have the necessary rights to write to it.
Uninstall your app (which should delete that directory) and then reinstall your app (which should recreate the directory with the correct permissions).
Requesting Permissions at Run Time
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.
More about runtime permission
Refer Answer
Hi firstly you have to check which android SDK version you are using
if it is less than 23 than you just have to put your permission in manifest file it work
if android version greater than 23 you should put all permission in manifest file as well as you should ask user permission for run time ( only first attempt )
for this you should follow this link https://stackoverflow.com/a/33162451/4741746
One more thing you can don is to change compileSdkVersion and buildToolsVersion to below 23 like 22 (but i will not suggest you for this because new features above 23 you can not be use )
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
}
}
if not working let me know
I'm using Samsung J7 Unrooted for Testing,
My AndroidManifest.xml contains the necessary permissions to write to external storage
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
I don't encounter any error when I used the code:
File myFile = new File("sdcard/Billing/mysdfile.txt");
It save the file on the Device Storage.
But when I specify the removable SDCard using the code:
File myFile = new File("/storage/extSdCard/mysdfile.txt");
I encounter the error :
open failed: EACCES (Permission denied)
I've tried different ways to point to the removable SDCard but encountered the same "open failed: EACCES (Permission denied)" error.
I've checked if I have the necessary permissions to write to external storages and it always say "Granted"
public void verifyStoragePermissions() {
if (ContextCompat.checkSelfPermission(InitActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getBaseContext(), "Denied", Toast.LENGTH_SHORT).show();
if (ActivityCompat.shouldShowRequestPermissionRationale(InitActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
} else {
}
}else{
Toast.makeText(getBaseContext(), "Granted",Toast.LENGTH_SHORT).show();
}
}
I've tried almost everything suggested listed in the google search but to no avail. There are a Few who advises to "Root" the phone but that is unfortunately not an option for me...
Hope someone can help me gain the needed permission rights to save files in my removable external storage without resorting to "Rooting" the phone.
On Samsung T230 (Android 4.4.2) It works in both of the following
File firstFile = new File("mnt/extSdCard/Android/data/com.example.storagetest", "test.txt");
RandomAccessFile fileLittle = new RandomAccessFile(firstFile, "rw");
File scndFile = new File("mnt/extSdCard/Android/data/com.example.storagetest", "test2.txt");
scndFile.createNewFile();
I've tried a lot of these devices are working in this way. T230 is an example
On Hometech Tablet (Android 5.1.1) does not work.
File firstFile = new File("mnt/external_sd/Android/data/com.example.storagetest", "test.txt");
RandomAccessFile fileLittle = new RandomAccessFile(firstFile, "rw");
File scndFile = new File("mnt/external_sd/Android/data/com.example.storagetest", "test2.txt");
scndFile.createNewFile();
I get an error as follows:
open failed: EACCES (Permission denied)
Current AndroidManifest.xml
<application>
...
</application>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I tried many different ways... requestPermissions() and android:maxSdkVersion does not change anything
Too much search but I could not reach a conclusion.
How can i create a file on SD Card.
Important: I can read files directly, but cant change or create new file.
How can i create a file on SD Card.
You don't. Devices that ship with Android 4.4+ do not allow arbitrary access to removable storage.
You can indirectly work with removable storage via the Storage Access Framework. There, you do not worry about exactly where the user is putting the data (external storage, removable storage, Google Drive, Dropbox, etc.).
I'm using BeagleBuddy mp3 tag editor to make changes to mp3 files on the internal / external storage... Works fine on some phones... but on certain phones, I get this error... (Samsung S4, Note 3) etc...
open failed: EACCES (Permission denied)
I have in my manifest...
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Still no luck... Any suggestions?
I've read a lot about how Android is protecting the external card from edits on some phones... is there a way to get around this?
I've used several different methods to get the external drive... Some work on some phone, some don't work...
This is what I am currently using:
public static File getRemovableStorage()
{
String value = System.getenv("SECONDARY_STORAGE");
if (!TextUtils.isEmpty(value))
{
String[] paths = value.split(":");
for (String path : paths)
{
File file = new File(path);
if (file.isDirectory())
{
return file;
}
}
}
return null; // Most likely, a removable micro sdcard doesn't exist
}
It finds all of the files on the external drive great... I just get that error when I try to update an mp3 tag on the external drive.