Reading from External Storage in native code

1,748 views
Skip to first unread message

Faraz Sherwani

unread,
May 31, 2014, 2:45:53 AM5/31/14
to andro...@googlegroups.com
Hello,

I've been trying to copy some assets to external storage in my Java code, and pass the directory to my native code to read from. However, the problem is, if there is no SD card on the device the getExternalFilesDir() returns something like "storage/emulated/0/Android/data/<my_app>/files", which when I pass to the native code to fopen and fread, causes the app to crash. I believe the reason is because the directory is artificial or a symbolic link or something of the sort. However, even if I use the stdlib.h function realpath() on the path passed down to the native code, it still crashes. If however, I manually replace emulated/0/ with emulated/legacy/ in the path, realpath() suddenly works and converts it to the actual location the files are stored, and the fread() works fine with no crashes.

The problem is I want to write code that is reliable in the long run, regardless of changes to the way Android organizes external storage and these symbolic links/virtual filesystems. I also want the code to work regardless of which device it is run on (I believe these emulated directories are different depending on the device).

Is this an oversight in the design of these virtual filesystems? Is there some way to get an external storage directory in Java that can be written to and reliably read from in native code as well?

Thank you for any help you can give me.

David Turner

unread,
Jun 2, 2014, 11:22:59 AM6/2/14
to andro...@googlegroups.com
On Sat, May 31, 2014 at 8:45 AM, Faraz Sherwani <farazs...@gmail.com> wrote:
Hello,

I've been trying to copy some assets to external storage in my Java code, and pass the directory to my native code to read from. However, the problem is, if there is no SD card on the device the getExternalFilesDir() returns something like "storage/emulated/0/Android/data/<my_app>/files", which when I pass to the native code to fopen and fread, causes the app to crash. I believe the reason is because the directory is artificial or a symbolic link or something of the sort. However, even if I use the stdlib.h function realpath() on the path passed down to the native code, it still crashes. If however, I manually replace emulated/0/ with emulated/legacy/ in the path, realpath() suddenly works and converts it to the actual location the files are stored, and the fread() works fine with no crashes.

Where does the code crash exactly? Are you saying that the call to fopen() or fread() crashes, or something bad happens after that? If the second, what is the result of errno after a failed fopen() / fread().
 
The problem is I want to write code that is reliable in the long run, regardless of changes to the way Android organizes external storage and these symbolic links/virtual filesystems. I also want the code to work regardless of which device it is run on (I believe these emulated directories are different depending on the device).

Is this an oversight in the design of these virtual filesystems? Is there some way to get an external storage directory in Java that can be written to and reliably read from in native code as well?

Thank you for any help you can give me.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at http://groups.google.com/group/android-ndk.
For more options, visit https://groups.google.com/d/optout.

Faraz Sherwani

unread,
Jun 3, 2014, 11:47:40 AM6/3/14
to andro...@googlegroups.com
fread() causes this:

"signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000004
Stack frame D/CrashAnrDetector( 4142):     #00  pc 00026204  /system/lib/libc.so
 (fread+39)"

fopen() doesn't seem to modify the errno value. It is 2 both before and after the call. fread() causes a crash so I cannot print the value of errno after it gets called.

J Decker

unread,
Jun 3, 2014, 11:49:09 AM6/3/14
to andro...@googlegroups.com
Check the return of fopen for NULL; if you pass a null file handle to most IO routines they segfault in most implementations.

J Decker

unread,
Jun 3, 2014, 11:49:29 AM6/3/14
to andro...@googlegroups.com
Then when you get a NULL, then read errno and find out it equals 2.

Faraz Sherwani

unread,
Jun 3, 2014, 11:53:19 AM6/3/14
to andro...@googlegroups.com
You are correct. fopen returns NULL. How can I get fopen to be redirected to the actual location of the files without explicitly changing emulated/0/ to emulated/legacy/? Why does the path returned by getExternalFilesDir() work in Java but not in native code?

J Decker

unread,
Jun 3, 2014, 11:56:47 AM6/3/14
to andro...@googlegroups.com
what is the path you are passing to fopen?  Are you sure it doesn't have a bad slash in it?  Are you sure you have the filename appended?  You shouldn't have to change directory... but you will have to specify the full path with a leading '/' which getexternalfilesdir should return.  Are you sure you're converting the java UTF string to a character string correctly?

Faraz Sherwani

unread,
Jun 3, 2014, 12:04:25 PM6/3/14
to andro...@googlegroups.com
Like I said in the post the path I pass is the same as the one I write to storage/emulated/0/Android/data/<my_app>/files/. The filename is appended in C.  If I print it out in C it comes out exactly where the file should be if that directory exists. If I call a path = path.replaceFirst("emulated/([0-9]+)/", "emulated/legacy/"); in Java before sending the path, it works fine. If I don't fopen() returns NULL causing a segfault at fread().

Doug

unread,
Jun 3, 2014, 2:14:35 PM6/3/14
to andro...@googlegroups.com
I can't help but notice your path isn't starting with a leading slash before "storage" every time you're entering it here.  Is that accurate?  Full paths should always start with a leading slash.

Have you tried using the java File methods getCanonicalFile/getCanonicalPath to resolve symlinks before passing the path down the C layer?

Doug

Faraz Sherwani

unread,
Jun 3, 2014, 2:17:50 PM6/3/14
to andro...@googlegroups.com
Yeah sorry it does start with a leading slash. If that was the problem then replacing emulated/0/ with emulated/legacy/ wouldn't work.

getCanonicalPath() and getAbsolutePath() return the same path as getPath() in this case so that doesn't help.

Faraz Sherwani

unread,
Jun 3, 2014, 2:36:26 PM6/3/14
to andro...@googlegroups.com
As the second answer in this thread states : http://stackoverflow.com/questions/15841380/android-disambiguating-file-paths

It seems to be a problem because there are two different mount points and not just symlinks. So using getCanonicalPath() doesn't work in this case.
Reply all
Reply to author
Forward
0 new messages