My issue is that on some devices, in this particular case on Android 10, stock Huawei p30 EMUI 10, I cannot create a new SD card file. documentFile.createFile(...) returns null, DocumentsContract.createDocument(...) throws a weird exception that I cannot find anywhere. It says "java.lang.IllegalArgumentException: Requested path ... doesn't appear under ...", as seen on the attached image.
Both mentioned functions have proper arguments, like mimetype and path. I don't have the device myself, I'm debugging it with a guy via email. At listing his storages there is no "/mnt/media_rw" mentioned anywhere, the SD card partition seems to be called "/storage/0123-4567". All other SD card file operations work, those extra SAF permissions are requested and handled properly. The code itself works just fine on the vast majority of devices, on my Android 10 too. I have target, compile SDKs set to 28, build tools 28.0.3.
Has anyone encountered such an issue? Any ideas how could it be solved? I'm afraid that it will be happening always more often as Android 10 grows and manufacturers continue doing things differently. Not to talk about the changes coming from SDK 29. Thanks
alright, so it looks like documentFile.createFile actually works, but it returns null. So just creating a brand new DocumentFile with the targeted path seems to work just fine.
Some additional info if someone encounters the same problem:
Context: As many Huawei users have noticed, the update from EMUI 9 to EMUI 10 has broken a number of apps that rely on write access to the SD card. The problem has been reported to Huawei, who shifted the blame onto Google and does not seem to be taking any further action. The developer of the app Total Commander, which was affected by the problem, found out that
On Huawei devices with Android 10, DocumentsContract.createDocument successfully creates the file, but then causes an exception
and suspects that
apparently Huawei hardcoded some paths where the user should be allowed to create files, but made a mistake.
Solution: As the question's author has already found, even though calling createFile() returns null, the file has been properly created. You can use findFile() to get it, for example:
// treeUri comes from the file picker
DocumentFile directory = DocumentFile.fromTreeUri(context, treeUri);
DocumentFile file = docFile.createFile(mimeType, filename);
if (file == null) {
file = docFile.findFile(filename + "." + mimeTypeExtension);
}
If at that point file is still null, then I think it's another issue.
Related
I have an Android app (developed under Xamarin.Android) which works fine when I test it locally on both emulators and physical devices. However, it fails on two physical devices (Moto G4, API 23, and Huawei Mate 9, API 24) when it goes through automatic testing on the Google Play Console. The problem arises when trying to create a log file in external storage. I have been running various tests under Firebase on the Moto G4, but cannot work out where the problem lies. I would be really grateful for at least one more pair of eyes on what I’m doing.
I check Android.OS.Environment.ExternalStorageState, which returns “MediaMounted”. I then use GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads) to work out where to create my log file. This returns “/storage/emulated/0/Download”. I next check that read/write permission has been granted. ContextCompat.CheckSelfPermission(this, Android.Manifest.Permission.ReadExternalStorage) and ContextCompat.CheckSelfPermission(this, Android.Manifest.Permission.WriteExternalStorage) both return Android.Content.PM.Permission.Granted. (In AndroidManifest.xml, I list <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />.)
At this point, I have established (I think) that the directory exists and that I have permission to write to it, so I try to create my log file, /storage/emulated/0/Download/LogFile.txt. This works fine locally, on both emulators and physical devices. It also works on most of the devices that the Google Console test puts to test. However, on the Moto G4 (physical device), file creation fails with the error “Could not find part of the path”.
I have also tried writing to DirectoryDocuments instead of DirectoryDownloads, which the Android documentation tells me is the “standard directory in which to place documents that have been created by the user”. The path returned for this is /storage/emulated/0/documents. When I try to write to that directory, it fails saying that the directory does not exist (which I believe is true).
Enumerating the contents of the Download directory, I noticed that there is a “Documents” directory inside the Download directory, i.e. /storage/emulated/0/Download/Documents. Writing to this directory again gives me the error “could not find part of the path”, same as writing to the Download directory itself.
My app absolutely has to store files somewhere (not just the log file). It is also important that the user can copy these files to/from the device, and I don’t want the files to be deleted, even if the app is uninstalled. For these reasons, I believe that external storage is the right (only) place for them.
I am at a loss to know what to try next. (I am wondering now whether there is actually an issue with the device, like a disk format issue.) If anyone has any suggestions on where to go from here, I would be really grateful! Extensive searches have not thrown up anything that I'm doing wrong, or any suggestion that the directory structure may be different on a Moto G4.
I'm facing an issue trying to get the app root folder, the problem comes in an emulator with Marshmallow. When I do context.getFilesDir() it returns a wrong path:
data/user/0/com.android.appname/files/
When it should be:
data/data/com.android.appname/files/
Somebody know why is this happening and how to solve it or another way to get the right path to the app root folder?
/data/user/0/com.android.appname/files/ is a perfectly valid path to be returned by getFilesDir(). That directory will be usable by your app.
adb is not your app. Its view of the filesystem will be different.
The exact location of where getFilesDir() maps to on the actual filesystem — or where tools like adb will see it — will vary based on several criteria, including OS version, which user account is running your app (remember: Android supports multiple users!), and possibly device manufacturer/custom ROM changes.
I'm using internalDataPath within my NativeActivity which when accessed at runtime looks something like this:
"/data/data/app.name/files"
I then try writing to the file like so:
- fopen("/data/data/app.name/files/test.txt", "wb");
- fwrite(buffer, size, count, m_handle);
- fclose(m_handle);
In this case, the fopen will fail and won't open the file which realistically means the other functions don't get called, I experimented by looking at the file system and instead, tried the following path.
"/Android/data/app.name/files/test.txt"
This works, it creates the file and allows for the other functions to write and close the file.
My question is, how come internalDataPath starts with /data/ instead of /Android/? Have I done something wrong in the setup of my application? Should I be calling a different function?
It's also worth mentioning that
externalDataPath = "/storage/emulated/0/Android/data/app.name/files"
Which means that for this device (which doesn't have an external SD) it points to the correct INTERNAL files path.
Let me know if any of this is unclear or you require more information.
Thanks
I added mkdir() to ensure the entire internalDataPath tree is definitely created and it seems to have worked on my Nexus 7, I can see the files being created with ES File Explorer.
It also works on my Galaxy S6, however I can't actually see the files in the ES File Explorer, but it works because the file is loaded and read upon the next restart so I can only assume I can't see the file because this device is not rooted.
It's worth noting that between versions of Android, internalData path has changed location, for example:
Nexus 7 (5.1.1):
internalDataPath = /data/data/app.name/files
Galaxy S6 (6.0.1):
internalDataPath = /data/user/0/app.name/files
These paths are absolute, or at least relative to the highest view-able root directory in ES File Explorer.
Not sure if this will help anybody, the main part of the problem was probably my own lack of knowledge of the Android file system since there seems to be lots of similarly structured directories, it's easy to get lost down the wrong one.
Thanks to Patrik and Dave for spending time looking into it with me.
On first time app launch I create a file using context.getFilesDir() to get the storage path.
This works fine, except for a very small number of cases this method returns the "/" root folder. As a result the app crashes with:
java.io.FileNotFoundException: /my_filename (Read-only file system)
When I debug on my device the file path is:
/data/data/my.package/files/my_filename
This code is called in the onCreate of a SherlockActivity. So the context is that of the activity. Strangely, the failure is rare and there is no commonality among the devices where it is failing.
Update:
Based on the feedback so far, this code is supposed to work and the occasional failures may be due to odd devices. Trying to work around this issue is an overkill for my use case where file storage is not really mandatory. I'll try to migrate my code to use SharedPreferences.
There is a confirmed bug in all pre-4.4 Android devices, which occurs rarely. The reason for the bug is a racing condition in creating app private directory on first launch.
The suggested fix (by Google Android team member) is to try the Context.getFilesDir() method again after first one failed with a "null" return value.
use
Environment.getExternalStorageDirectory().getAbsolutePath();
to get the external storage directory.
use
Environment.getCacheDir();
for using the application's sandboxed cache directory.
I've got a pretty thorny problem with Android 2.3: I have an app that gathers various logs for debugging and support purposes (my company does Linux for rugged hardware), and has stopped working lately, because it's failing to write to the SD card. Here are the symptoms I've seen and the investigations I've carried out:
Happens across multiple devices of multiple types with different SD cards, all of which have been checked for filesystem corruption (no issues found).
All devices report: Environment.getExternalStorageState() equals Environment.MEDIA_MOUNTED.
All devices also report that Environment.getExternalStorageDirectory().getAbsolutePath().canWrite() is false.
Via PackageManager.checkPermission(), my app reports that it has the WRITE_EXTERNAL_STORAGE permission.
OI File Manager is able to create directories and move files on the SD card; my app can do neither.
This code is sufficient to cause a failure:
String sdcardDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
File directory = new File(sdcardDirectory + "/logger");
if(!directory.mkdirs()){
//fails here.
Log.w("Logger", "Could not create logger directory.");
}
Since I have access to the keys for this device, I even went so far as to sign the app with the platform key and run it as android.uid.system, with no luck. Anyone have any ideas?
It turns out this is a case partly of bad diagnosis on my part, and partly an apparent change in 2.1 to 2.3.
The bad diagnosis was that the directory above was indeed being created. The apparent change between 2.1 and 2.3 may be Android internally, or it may be the way we're setting up paths, PATH, and symbolic links in our own builds. Further down from the code in the original post, there are a few calls to exec() to get e.g. output from logcat and copies of various bits of useful information in /proc; using absolute paths to the commands fixed the problem.
Thanks for the help in ruling things out.