I have to develop a little function that, besides other things, writes a file in the SD card. I have to use it for two specific Android tablets provided by a supplier. One tablet uses Android 5 and the other uses Android 7. The application that I am modifying is a system app and it doesn't have UI. I'm calling the code from a Service, and I want to call it from a FirebaseMessagingService. I have problems to write a file only in Android 7 tablet.
I have no problems with the Android 5 tablet, I identified the external storage folder and I can create files in it. But I do have problems in Android 7, I identified the external storage folder and I have a problem: Permission denied.
I have this permissions in the manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
This is the piece of code that is giving me problems:
public void myFunction()
{
String sdPath;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
sdPath = "/storage/extsd";
else
sdPath = "/storage/0665-3426";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (my_context.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "Permission is granted");
else
Log.d(TAG, "Permission is denied");
}
File folder = new File(sdPath);
if (folder.exists()) {
Log.d(TAG, sdPath + " exists, can write: " + folder.canWrite());
File file = new File(sdPath + "/new_file");
boolean fileExists = file.exists();
Log.d(TAG, file.getAbsolutePath() + " file exists: " + fileExists + ", can write: " + file.canWrite());
if (!fileExists) {
try {
file.createNewFile();
Log.d(TAG, "Can write in " + sdPath);
}
catch (Exception exception) {
Log.e(TAG, "Cannot write in " + sdPath + ": " + exception.getMessage());
}
}
}
else
Log.e(TAG, sdPath + " does not exist.");
...
}
Here the logs in Android 5 tablet:
10-22 14:44:51.271 10450-10450/com.my.app D/MY_TAG: /storage/extsd exists, can write: true
10-22 14:44:51.368 10450-10450/com.my.app D/MY_TAG: /storage/extsd/new_file file exists: false, can write: false
10-22 14:44:51.479 10450-10450/com.my.app D/MY_TAG: Can write in /storage/extsd
And here the logs in Android 7 tablet:
2020-10-22 15:11:56.383 19689-19689/com.my.app D/MY_TAG: Permission is granted
2020-10-22 15:11:59.037 19689-19689/com.my.app D/MY_TAG: /storage/0665-3426 exists, can write: false
2020-10-22 15:12:07.956 19689-19689/com.my.app D/MY_TAG: /storage/0665-3426/new_file file exists: false, can write: false
2020-10-22 15:12:07.957 19689-19689/com.my.app E/MY_TAG: Cannot write in /storage/0665-3426: Permission denied
As you can see, even if permission is granted, canWrite() method returns false in Android 7. Do you know the reason? How can I solve this problem?
I have read some other questions from stack overflow but I didn't find the solution.
I'm referring to one of the answers in this Stack Overflow thread.
I am not aware of the target SDK version in your case, but if you're building for version 29, try adding this to your AndroidManifest.xml file:
<manifest>
<application
<!-- other stuff -->
android:requestLegacyExternalStorage="true">
<!-- other stuff -->
</application>
</manifest>
Also, are you requesting permissions at runtime correctly?
Removable micro sd cards are read only since Android Kitkat/Lollipop.
Hence you cannot write to paths like "/storage/0665-3426".
Only one app specific directory is writable on a removable micro sd card.
To determine the path of that folder have a look at the second item returned by
getExternalFilesDirs()
I have decided not to use the Android API. Since the application has elevated privileges, I have created the file by executing a shell command. This is the code to create a file (works with removable SD card folder):
public static String createFile(String filePath)
{
String returnValue = "";
try
{
Runtime runtime = Runtime.getRuntime();
String[] command = new String[]{ "su", "0", "touch", filePath};
Process p = runtime.exec(command);
p.waitFor();
java.io.BufferedReader errorIn = new java.io.BufferedReader(
new java.io.InputStreamReader(p.getErrorStream()));
String line = "";
while ((line = errorIn.readLine()) != null)
returnValue += line + "\n";
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
}
return returnValue;
}
Related
I am trying to write an image to my android file system, however when trying to write the bytes, I get the above error.
I am running Visual Studio 2019 (as administrator) and targeting API Level 29
AndroidManifest.xml
My external storage permissions are present in my manifest file:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
And as per another suggestion I have added android:requestLegacyExternalStorage="true" in my application tag:
<application android:label="App Name" android:icon="#mipmap/launcher_foreground" android:extractNativeLibs="true" android:requestLegacyExternalStorage="true">
MainActivity.cs
Here I am requesting the permissions when the app starts and pressing allow on both prompts:
int requestPermissions = 0;
string cameraPermission = Android.Manifest.Permission.Camera;
string filePermission = Android.Manifest.Permission.WriteExternalStorage;
if (!(ContextCompat.CheckSelfPermission(this, cameraPermission) == (int)Permission.Granted) || !(ContextCompat.CheckSelfPermission(this, filePermission) == (int)Permission.Granted))
{
ActivityCompat.RequestPermissions(this, new String[] { cameraPermission, filePermission
}, requestPermissions);
}
SaveMedia.cs
I get the error on File.WriteAllBytes:
public string SavePickedPhoto(Stream file, string fileName)
{
var bytes = GetBytesFromStream(file);
string path = Path.Combine(Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath + "PhotoDirectoryForApp";
if (!Directory.Exists(path))
{
try
{
Directory.CreateDirectory(path);
}
catch (Exception e)
{
}
}
Path.Combine(path, fileName);
try
{
File.WriteAllBytes(path, bytes);
}
catch (Exception ex)
{
}
return path;
}
I am at a bit of a loss on what to try next as to me it seems that I should have the necessary permissions to write a file to a folder on the device. I have also tried various locations to save to and methods of saving with the same result.
Thanks in advance
You are trying to write to a directory as you are throwing away the return value from Path.Combine :
Path.Combine(path, fileName);
Try:
~~~
path = Path.Combine(path, fileName);
~~~
I know this is a many times asked question in Stackoverflow and I have seen those questions. The answers suggested checking the permission, restarting device, check if the parent directory exists etc. I tried all of them and its still not working.
I have Manifest.permission.WRITE_EXTERNAL_STORAGE permission specified in manifest.
The following is my code.
File newFile = new File(parent.getCanonicalPath() + "/" + dirName + "/");
if(!newFile.exists()){
boolean created = newFile.mkdirs();
if(!created){
int permission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
boolean permissionGranted = (permission == PackageManager.PERMISSION_GRANTED);
Log.e(getClass().getSimpleName(), "Could not create directory "
+ ", Parent exists : " + parent.exists()
+ ", Parent Dir writable : " + parent.canWrite()
+ ", Permission granted : " + permissionGranted);
}
}
And the log prints
Could not create directory, Parent exists : true, Parent Dir writable : true, Permission granted : true
I have gained write permission to sdcard through its TreeUri and then I converted the tree uri to actual path to use it with the File class.
My minSdkVersion is 19 and targerSdkVersion is 25
What am I doing wrong?
Edit :
I tried all of the above suggestions but failed. Now, I fixed the issue by using DocumentFile. I can now create new directories and files. But still I am not sure about what's happening with File. Can anyone tell me what is happening?
I did the test on,
Device : Lenovo A2010-A
Android version : 5.1
Replace
boolean created = newFile.mkdirs();
with
boolean created = newFile.mkdir();
You should not use getCanonicalPath and use getAbsolutePath instead. Below is how I create a new folder:
File newFile = new File(parent,dirName);
if(!newFile.exists()){
boolean created = newFile.mkdirs();
}
Replace parent.getCanonicalPath() to Environment.getExternalStorageDirectory()
File myDirectory = new File(Environment.getExternalStorageDirectory(), dirName);
if(!myDirectory.exists()) {
//create file if not generated
myDirectory.mkdirs();
}else {
//not created file
}
i need to save an image from camera on android.
i used the write external storage permission in manifest and i am using this code
File dir = new File(Environment.getExternalStorageDirectory(), "Test");
if (!dir.exists() || !dir.isDirectory())
dir.mkdirs();
String path = dir.getAbsolutePath();
Log.d(TAG, path); //log show the path
File file = new File(dir.getAbsolutePath() + "/Pic.jpg");
Log.d(TAG, file.getAbsolutePath()); //again path is shown here
outStream = new FileOutputStream(file);
outStream.write(bytes);
outStream.close();
Log.d(TAG, "onPictureTaken - wrote bytes: " + bytes.length); //fail here
} catch (FileNotFoundException e) {
Log.d(TAG, "not done"); //error is here (this exception is thrown)
} catch (IOException e) {
Log.d(TAG, "not");
} finally { }
i also tried mkdir() instead of mkdirs() same result.
any idea what went wrong in the code?
thanks
For those not as experienced like me. I fought this issue, lost hair for some time. I am targeting api 21 (for compatibility sake) and it worked on lollipop but on marshmallow it would not create the directory. I did have the "uses" permission in the manifest but it still would not work. Apparently in Marshmallow when you install with Android studio it never asks you if you should give it permission it just quietly fails, like you denied it. You must go into Settings, apps, select your application and flip the permission switch on.
Some one like me who was trying in Android10. Please use below API in manifest:
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
Latest Update From Google:
After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag.
Did you put
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
in your AndroidManifest? If you are using android M you must request user permission to write on sd, look here an example
IDIOT ME! i have used the Manifest Permission but when installed the app on phone i didnt grant permission for storage!... i understand a negative on this question... but i hope if someone else face the same..check your phone permission. sorry all for inconvenience.
you have created directory, not file. Create new file with following code
File file = new File(dir.getAbsolutePath() + "/Pic.jpg");
file.createNewFile()
if you are testing on android M, you should probably check Settings > App > Permission to see if permission to access storage is granted. This saved me.
if you already allowed R/W permission(Runtime Permission too) and still doesn't work add this below mentioned line in your AndroidManifest.xml
<application
........
........
android:requestLegacyExternalStorage="true">
Note: this must required if you'r targeting Android 10+
Starting from API 30 you can only write in your app-specific files
File dir = new File(context.getFilesDir(), "YOUR_DIR");
dir.mkdirs();
or in the external storage of your app Android/data
File dir = new File(myContext.getExternalFilesDir("FolderName"),"YOUR_DIR");
UPDATE
this answer provided another solution https://stackoverflow.com/a/65744517/8195076
UPDATE
another way is to grant this permission in manifest
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
like this answer https://stackoverflow.com/a/66968986/8195076
Try this. Provide runtime permission for marshmallow it is perfectly work in my Application code :
private String getFilename(String strFileName) {
String filepath = Environment.getExternalStorageDirectory().getPath();
File fileBase = new File(filepath, "Test");
if (!fileBase.exists()) {
fileBase.mkdirs();
}
return (file.getAbsolutePath() + "/" + strFileName + file_exts[currentFormat]);
}
new File(getFilename(edt.getText().toString().trim()))
outputFile = new File(apkStorage + "/" + downloadFileName );
//Create Output file in Main File
//Create New File if not present
if (!outputFile.exists()) {
isExternalStorageWritable();
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
Log.e(TAG, "File Created");
OutputStream fos = new FileOutputStream(outputFile);//Get OutputStream for NewFile Location
InputStream fis = c.getInputStream();//Get InputStream for connection
byte[] buffer = new byte[1024];//Set buffer type
int len1 = 0;//init length
while ((len1 = fis.read(buffer)) >0) {
fos.write(buffer, 0, len1);//Write new file
}
//Close all connection after doing task
fos.close();
fis.close();
I wrote this code for creating a file, but it is not working in android 11
when writing code for android API 29 and above use the following permission in your application manifest (AndroidManifest.xml)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
Adjust your code to read like the following
ActivityCompat.requestPermissions(this, new String[]
{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
},
PackageManager.PERMISSION_GRANTED);
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
file = new File(Environment.getExternalStorageDirectory().getPath(), "TestDirectory/Document/");
if (!file.exists()) {
try {
file.mkdirs();
} catch (Exception e) {
e.printStackTrace();
}
}
In an Android application (API level 10),I do not use below permission in manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
So application would not write on root of external storage.But when I try to check read/write permission via AccessController by this code:
File f = Environment.getExternalStorageDirectory();
String path = f.getAbsolutePath();
String actions = "read,write";
try {
AccessController.checkPermission(new FilePermission(path, actions));
Log.d("Tag", "You have read/write permition to use : " + path);
} catch (AccessControlException e) {
Log.d("Tag", "You have not read/write permition to use : " + path);
} catch (SecurityException e) {
Log.d("Tag", "You have not read/write permition to use : " + path);
}
Result will be:
You have read/write permition to use : /storage/sdcard0
So why AccessController.checkPermission does not throw exception on root of External Storage when application has no permission to write on it?
So I am writing a profiler that needs to be able to log exceptions during the profiling session. My plan was to use logcat to dump to a file either on the SD card or the internal storage and then when the profiling session was complete, zipping the file up and sending it up to the server. I added the android.permission.READ_LOGS in my manifest, and my code is as follows:
public void startLoggingExceptions() {
String filename = null;
String directory = null;
String fullPath = null;
String externalStorageState = null;
// The directory will depend on if we have external storage available to us or not
try {
filename = String.valueOf(System.currentTimeMillis()) + ".log";
externalStorageState = Environment.getExternalStorageState();
if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) {
if(android.os.Build.VERSION.SDK_INT <= 7) {
directory = Environment.getExternalStorageDirectory().getAbsolutePath();
} else {
directory = ProfilerService.this.getExternalFilesDir(null).getAbsolutePath();
}
} else {
directory = ProfilerService.this.getFilesDir().getAbsolutePath();
}
fullPath = directory + File.separator + filename;
Log.w("ProfilerService", fullPath);
Log.w("ProfilerService", "logcat -f " + fullPath + " *:E");
exceptionLogger = Runtime.getRuntime().exec("logcat -f " + fullPath + " *:E");
} catch (Exception e) {
Log.e("ProfilerService", e.getMessage());
}
}
exceptionLogger is a Process object, which I then call exceptionLogger.destroy() on when the profiling session is complete.
The behavior I am seeing is that the file is created where I specify but there is never any logging output in the file. I am using the dev tools application in the emulator to force an unhandled exception to show up in the logs, but my logcat output file remains empty. Any thoughts?
EDIT: So when I go into the adb shell, and then SU to the user account that is assigned to my application, I see the following when running logcat:
Unable to open log device '/dev/log/main': Permission denied
I had thought that adding the manifest permission to my app would then allow me to do this?
And the moral of the story is: double check that your permission in your manifest file actually says READ_LOGS and not just READ_LOG.
Just in case somebody runs into the same problem I had: When trying to read logcat from a Unit test, the permission needs to be added to the application under test, not the test project. Adding the permission to the test project only will still give permission errors.