In my app, users pick files. Internally, I store information about the file, which I key based on the file path. Next time that file is used, I do stuff with the stored information. Trouble is I instantiate my files with:
File file1 = new File(Environment.getExternalStorageDirectory() + "/test.txt");
And then, on a particular JB device, file1.getCanonicalPath() gives: "/storage/emulated/0/test.txt".
The trouble is that when other apps launch my app with a file path in an Intent, the paths they send tend to look like: "/mnt/sdcard/test.txt".
Is there a smart strategy to disambiguate these two paths? Possibly I should be instantiating my files differently?
Edit:
The trouble is, the two canaonical paths for the two files are not equal. For the below, cp1=="mnt/sdcard/test/txt"
and cp2=="/storage/emulated/0/text/txt"
:
File file1 = new File("/mnt/sdcard/test.txt");
File file2 = new File("/storage/emulated/0/test.txt");
String cp1 = file1.getCanonicalPath();
String cp2 = file2.getCanonicalPath();
Take a look at the answer here. It also uses canonical path, but in a slightly different manner that might work for you
First, the only correct way to get the external path is using the
getExternalStorageDirectory
and othergetExternalStorageXXX
in Android.Android will firstly try to resolve two system variable:
while the
ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"
andENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET"
. If theEMULATED_STORAGE_TARGET
variable is set, it means the device has emulated storage then the storage path will beEMULATED_STORAGE_TARGET
.(After Android 4.2, it support multiple-user external storage, then there would be a /0 or 0 after the path) But if it is not set andEXTERNAL_STORAGE
is set, the path will beEXTERNAL_STORAGE
. If both of them are not set, the path will be/storage/sdcard0
by default. So different devices may contain different paths for external storage.As External Storage Technical Information says, you can customize the storage of the device by setting up the init.rc file. For example in the default goldfish one:
If you use
getExternalStorageDirectory
you will get/mnt/sdcard
, but/sdcard
is a symbolic link to that directory.So in your case, the init.rc may contain:
So they are not ambiguous, they are actually same.
I think the getCanonicalPath() might work for the vast majority of your use cases.
There is probably no easy solution for this. The problem is that apparently there are two different mount points in the file system that actually point to the same location. File.getCanonicalPath() can only resolve symbolic links but not different mount points.
For example on my Nexus 4 this code:
prints
I used this code to exec "mount" and print the output:
The relevant output is:
On newer Android versions, SD storage is available from many paths, for example:
If you check on which device those paths are located, some are on different devices (because of fuse 'virtual' filesystem), hence getting their canonical path doesn't lead to the same file path, even though they are actually the same files.
Furthermore, it appears that on Marshmallow, things get worse and even file path under /sys (full of redirections/links) are not reported properly and getCanonicalPath() returns the original path instead of actual canonical path.
While a ls -l (or readlink) on the given file path will show the actual canonical path, the API doesn't work anymore. Unfortunately running a readlink or ls -l is very slow (average of 130ms when the shell is already running), compared to an already slow, but much faster getCanonicalPath(), it's a pity.
Conclusion, getCanonicalPath is broken and has always been broken.