Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android getExternalStorageDirectory() returns invalid directory

Tags:

android

I am trying to add OBB file support and I have run into a bizare situation. When I call getExternalStorageDirectory(), it returns a path that does not exist on my device.

I call this at the start of my app:

Log.i( LOG_ID, "XXXXXXXXXXXXXXXX <--- "+Environment.getExternalStorageDirectory().getAbsolutePath() );

It returns:

XXXXXXXXXXXXXXXX <--- /storage/emulated/0

But when I connect via adb -d shell and run ls -al /storage/emulated I get:

lrwxrwxrwx root     root              2013-12-04 15:17 legacy -> /mnt/shell/emulated/0

As you can see the path getExternalStorageDirectory() returned is not valid on my device. Any idea why this would be and how I can locate the OBB files without hard coding /mnt/sdcard/?

EDIT:

I have just run some further testing:

Works - stat( "/sdcard/Android/obb" );
Fails - stat( "/sdcard/Android/obb/uk.co.mycomp.myapp" );

shell@android:/mnt/shell $ ls -ald /sdcard/Android/obb/                        
drwxrwxr-x root     sdcard_rw          2013-12-18 16:42 
shell@android:/mnt/shell $ ls -ald  /sdcard/Android/obb/uk.co.mycomp.myapp
drwxrwxr-x root     sdcard_rw          2013-12-18 18:36

As you can see the two directories are identical yet I can only access the parent. This may or may not have any relevance to my issue...

like image 626
Paul Norman Avatar asked Dec 18 '13 16:12

Paul Norman


1 Answers

In recent Android versions, the mounts are actually different for different user ID's, which invalidates the way you are trying to investigate this.

Your attempt at investigating the storage paths is frustrated by the fact that recent Android versions leverage the "Per-process namespaces" feature of the Linux kernel to provide different sets of mounts to different process ancestries. This is likely connected to the efforts towards supporting multiple (human) user accounts, with both unique and shared sub-sections of External Storage.

When you run an adb shell, you get a set of mounts inherited from adbd.

But in contrast, code run by an application (either in it's process, or a child process) gets a different set of mounts, apparently either inherited from zygote or configured soon thereafter by the process differentiation code which turns a freshly budded zygote into your app's process.

To get an idea of what mounts your app sees, install something like connectbot which you can use to get a local shell descended from an application process, and run the mount command, or any other investigation of interest. For example:

u0_a99@build:/ $ mount

/dev/fuse /storage/emulated/0 fuse rw....
/dev/fuse /storage/emulated/legacy fuse rw.....

In comparison, checking from the adb shell:

shell@build:/mnt $ mount

/dev/fuse /mnt/shell/emulated fuse rw.....

Confusingly, because the mounts are determined by the processes's ancestry rather than it's user id, using the run-as tool from adb will not get you the same view as an application process or it's child, but rather instead will give you adb's view.

However, if you have the pid of a running app's process, you can use run-as to look in it's /proc/pid##/mounts file, without having to actually get a shell descended from the app.

like image 199
Chris Stratton Avatar answered Oct 04 '22 08:10

Chris Stratton