A working app of mine started throwing warnings to logcat all of a sudden on my 4.4.4 Kitkat device:
W/ContextImpl﹕ Failed to ensure directory: /storage/emulated/0/Android/data/com.example.app/files/Pictures
All the photos and other data are unaccessable to the app.
After some digging it turns out that there's a seemingly 0 byte file in /storage/emulated/0/Android/data with the package name of my app: com.example.app. No wonder that Android can't create a directory with the same name.
I have absolutely no idea how, when and this file got created. Or better to say, how, when and why the original directory got corrupted.
The strange thing is, that even though it's listed when I call either list() or listFiles() on the data dir itself, calling exists(), isFile(), isDirectory() on the file itself will all return false.
The file seems to have no uid or gid and no date/time associated with it. It can neither be renamed nor deleted. Trying to clear the data of the app will also not remove it, nor will uninstalling the app.
What to do now? Changing the package name of the app so that a new directory can be created is obviously not an option here.
Rebooting removed the file, solving the problem. Still no idea how and why this happened though.
Related
I have an app that write JSON backups and read them in production since december 2020.
Lately, without any update for this part, the behaviour of Ionic File changed.
I figured out in another thread that apps aren't allowed anymore to read and write in root directory since Android 11, so I changed it to the root/Documents directory, but I sitll have a strange issue.
Here's the behaviour: the app can write and read the file, and re-write it if necessary.
Everything's going well until I uninstall and reinstall the app, then neither read or write work.
In this case, IonicFile.readAsText returns null even if the file exists and is filled.
https://github.com/sebferrer/life-notes/blob/master/front/src/app/infra/importer-exporter.service.ts#L86
And IonicFile.writeExistingFile returns an error with the message "undefinedNaN".
https://github.com/sebferrer/life-notes/blob/master/front/src/app/infra/importer-exporter.service.ts#L146
Here's a workflow to help understand the issue:
I install the app. The file doesn't exist yet.
I try to read the file via IonicFile.readAsText: that throws the NOT_FOUND_ERR error. This is the expected behaviour as the file doesn't exist yet.
I create the file with IonicFile.createFile. Success.
I write on it via IonicFile.writeExistingFile. Success.
I write on it again via IonicFile.writeExistingFile. Success.
I read it via IonicFile.readAsText. Success: IonicFile.readAsText returns the file content.
I uninstall the app. The file created before still exists and didn't change.
I reinstall the app. The file created before still exists and didn't change.
I try to read the file as before via IonicFile.readAsText. Unexpected behaviour: no error is thrown, but IonicFile.readAsText returns null instead of the file content.
I try to write on the file as before via IonicFile.writeExistingFile. Unexpected behaviour: IonicFile.writeExistingFile throws an error this "undefinedNaN" message.
It's like when I uninstall and reinstall the app, the reinstalled app haven't any R/W right about the files generated before. But it worked perfectly before.
I'm so confused right now, any help would be more than welcome.
Edit:
It appears to be a permission issue introduced by the newer versions of Android (see comments below).
Scenario: After running my app once, I go into the Settings->Apps->App and click Clear Data. Upon trying to run the app again it fails to restore some required files for the app to run properly. The call to context.getExternalFilesDir(null) returns a null pointer. Attempting to create a file using the null pointer tries to create the file in the root folder (ie: "\myFileName.txt") which is not where I want the files to go and a read only part of the system.
After looking through numerous questions on here I can cross of that
This is not a permission issue, if I do not clear the data I can write files till my hearts content.
This also does not seem to be a 'we have left a lock or reference to the file' issue as the folder does not exist, I am not getting a lstat error and lsof is listing no files, even if I can not follow the links, under my package's name.
When I start the app the second time and it fails to write the files because of the null pointer, the system is left in its broken state and only a device reset appears to fix it. If I browse to the Android/data folder in an android file explorer I see a file, not a folder, called 'com.myapps.packag' there. But it has no associated information (It shows up as a ? icon, 0.00B in size, -rw for its stats and no date created/updated.. this looks like empty default information to me).
If I browse into the folder via ADB shell access and do an ls -al on the data folder I get the error in the title:
lstat 'com.myapps.package' failed: Device or resource busy
and that is it.
lsof reports no files open by anything under the packages name. Trying to scan the list of files seems fairly useless as most of them will not resolve (My device is not rooted so I can not get root access to have it display the information properly).
Basically I am at the end of things I could find on here and through Google to try out and see why the second running of the app appears to be failing to create its package folder.
Oh, if it matters I am using Android OS 4.2.2 (API 17)
Can you check what is the state of the media by using Environment.getExternalStorageState()
You can then understand the current state of connection of your app with the external storage.
Returns the current state of the primary "external" storage device.
Returns
one of MEDIA_UNKNOWN, MEDIA_REMOVED, MEDIA_UNMOUNTED, MEDIA_CHECKING, MEDIA_NOFS, MEDIA_MOUNTED, MEDIA_MOUNTED_READ_ONLY, MEDIA_SHARED, MEDIA_BAD_REMOVAL, or MEDIA_UNMOUNTABLE.
http://developer.android.com/reference/android/os/Environment.html#getExternalStorageState%28%29
We've been successfully using the APK expansion file technology in our app for some time by expanding the .obb file into another directory for reading of the various pieces. We finally decided to try reading directly out of the bob by mounting it using StorageManager. The code we are using seems pretty common on the net.
We make the call:
if ( storageManager.mountObb( obbFile.getAbsolutePath(), null, obbListener ) )
{
Log.d( "STORAGE_MNT", "SUCCESSFULLY QUEUED" );
}
else
{
Log.d( "STORAGE_MNT", "FAILED" );
}
and this succeeds as "SUCCESSFULLY QUEUED". However the obbListener is never called afterwards. If you look in the LogCat you do see the following:
Calling a method in the system process without a qualified user:
android.app.ContextImpl.bindService:1543
com.android.server.MountService$ObbActionHandler.connectToService:2458
com.android.server.MountService$ObbActionHandler.handleMessage:2337
android.os.Handler.dispatchMessage:102 android.os.Looper.loop:136
I assume this is related to the problem but I have been unable to find much on why there is no "qualified user". Can someone please explain what could be going on here?
We have verified that obbFile.exists() is true. This file is there.
While searching for an answer I've see lots of references to broken a JOBB tool. It also seems like Android 4.4 had a bug that prevented this from working (although I'm using 4.4.2 right now). I'm wondering if this is stable enough to use for production code.
Well, I think I finally figured out what is happening. It looks like the obb file in question was already mounted. The file was an APK expansion file and it would appear this is automatically mounted for you by the system.
If, before trying to mount it, I execute:
if (storageManager.isObbMounted( obbFile.getAbsolutePath() ))
{
Log.d("", "obb file mounted at " + storageManager.getMountedObbPath( obbFile.getAbsolutePath() ));
}
without first explicitly mounting it, I get a valid mount path. I really wish the docs (or LogCat messages) were more explicit about this. It took me about a day to discover this. It wasn't until I explicitly used an (necessary) encryption key on the obb that I saw a message in LogCat saying the file was already mounted.
Another reason for the listener not being called is if the OBB is invalid - such as not being created with JOBB using the correct package name. In my case I messed up creating the test environment and so was attempting to mount a pre-Storage Manage / JOBB expansion file created the old way with zip. I once got an internal error event, but only once out of hundreds of hair-pulling attempts. It seems that the Storage Manager will fail silently after successfully queuing the mount and never call the listener. As soon as I went back to basics and redid everything from scratch I spotted the invalid .obb file, fixed it, and it all worked as documented.
Note that I also have a background task that tests for the expansion file being mounted as a fallback running once a second. I found this idea from a web article somewhere. Often the background task spots the OBB expansion is mounted way before the Storage Manager listener callback is invoked.
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.
In an Android app, using NDK, I'm accessing a local file using fopen, fclose, etc. My app started out as HellloJNI (the supplied example). Accessing the file actually worked initially. The file path I used was "/data/data/com.example.hellojni/files/Test.txt".
Naturally I don't want my app to be called HelloJNI forever, so I changed the package name (manifest/#package in AndroidManifest.xml) from com.example.hellojni to com.example.mytest. Accordingly, I use a different file path: "/data/data/com.example.mytest/files/Test.txt". But the same call to fopen("[...]", "w+") that used to succeed now fails.
Any ideas?
It works after manually creating the files folder below /data/data/com.example.mytest. In a way, it's obvious, but I still don't fully understand it, because I never created explicitly the files folder below /data/data/com.example.hellojni. So where did that one come from?
Edit:
Apparently calling getFilesDir creates the Files folder implicitly, so that mystery is solved :-)