/storage/emulated/0/ is actually
/data/media/0/ exposed through an emulated / virtual filesystem, not the actual one.
This is with reference to my previous answer here, but with more relevant details.
On Android 5:
/sdcard >S> /storage/emulated/legacy >S> /mnt/shell/emulated/0 /mnt/shell/emulated >E> /data/media
On Android 6+:
# for (Java) Android apps (running inside zygote virtual machine) # "/storage to VIEW" bind mount is inside a separate mount namespace for every app /sdcard >S> /storage/self/primary /storage/self >B> /mnt/user/USER-ID /mnt/user/USER-ID/primary >S> /storage/emulated/USER-ID /storage/emulated >B> /mnt/runtime/VIEW/emulated /mnt/runtime/VIEW/emulated >E> /data/media # for services/daemons/processes in root/global namespace (VIEW = default) /sdcard >S> /storage/self/primary /storage >B> /mnt/runtime/default /mnt/runtime/default/self/primary >S> /mnt/user/USER-ID/primary /mnt/user/USER-ID/primary >S> /storage/emulated/USER-ID /storage/emulated >B> /mnt/runtime/default/emulated /mnt/runtime/default/emulated >E> /data/media
>S> for symlink,
>E> for emulated and
>B> for bind mount
USER-ID of current user in case of
Multiple Users or
Work Profile, normally
0 i.e. that of device owner
VIEW is one of
read (for apps with permission.READ_EXTERNAL_STORAGE) or
write (permission.WRITE_EXTERNAL_STORAGE) or
default (for processes running in root/global mount namespace i.e. outside zygote)
* There were minor differences on previous Android versions but the concept of emulation was same ever since implemented.
* For a little bit more details on Android’s mount namespace implementation, see this answer.
/storage/emulated/0 – which represent a FAT/vFAT/FAT32 filesystem – point towards
/mnt/expand/(UUID)/media/0 in case of Adoptable Storage) through
Being not Android specific but generally Linux related, symlink and bind mount (see “Creating a bind mount”) are out of the scope of this question, as the question is about emulation part mainly.
Why the emulation is here? Emulated filesystem is an abstraction layer on actual filesystem (
f2fs) that serves basically two purposes:
- Retain USB connectivity of Android devices to PCs (implemented through MTP now a days)
- Restrict unauthorized access of apps/processes to user’s private media and other apps’ data on SD card.
Read Android’s Storage Journey for details, the summary is:
Early Android devices were short on internal storage and relied on (physically) external SD cards that traditionally use FAT family of filesystem to ensure compatibility with most of the PCs (refer to Microsoft’s dominance on PC world).
When the internal storage grew in size, same filesystem was shifted to internal (still called “external”) SD card.
But the FAT/vFAT implementation had two major issues which were addressed by Google gradually:
- Android devices were connected to PCs directly (USB Mass Storage) just as we connect a USB drive these days. UMS exposes the device at block level and disconnects the SD card from Android framework (un-mounts), thus making whole data unavailable to apps and possibly breaking many functionalities.
- FAT (being Windows’ favorite in development days) was never designed to enforce UNIX permissions (mode, uid, gid and likewise symlinks, and
FS_IOC_FIEMAP). So, all data on SD card was available to all apps (since every Android app is a UNIX/Linux user and has a uid) with no restrictions, hence raising serious privacy and security concerns.
Both of these issues were addressed through emulation:
- Actual SD card storage was moved to
/datapartition (or independent /sdcard partition on some devices previously) which holds
ext4filesystem (gradually being replaced by
f2fs), fully implementing UNIX permissions.
- This design made using UMS impossible because whole
/datapartition could not be exposed to PC for 2 more reasons:
(1)it contains a lot of settings and apps’ data which is to be protected from other apps as well as human users.
(2)Linux filesystems are not supported by Windows.
So UMS was replaced with Media Transfer Protocol which is a client-server type extension to PTP – an already established protocol. MTP doesn’t expose block device but works through software stack. MTP host runs on Android as an app (
android.process.media) fully sandboxed in Android framework, not capable of doing any escalated tasks.
Now the apps (and MTP, which is also an app) interact with emulated storage instead of
/data/media, achieving both purposes at the same time i.e. enforcing permission checks underneath and looking like FAT filesystem on upper surface.
Google is now implementing emulation through sdcardfs to overcome shortcomings of FUSE; one major being the input/output overhead i.e. to improve read/write speeds.
EXTERNAL STORAGE PERMISSIONS:
Concept of Public and Private files on external storage can be demonstrated using an example:
Install Termux app.
Execute following commands:
* You should have WhatsApp installed or select some other app’s private folder.
Now Force Stop the Termux app and grant Storage permission. Execute the commands again:
See the difference in permissions of same files and directories. This seems not to be simply possible without emulation on a native Linux filesystem when there are hundreds of apps (users) to be dealt with simultaneously. This is the filesystem emulation that lets the same file to be exposed with three different sets of permissions at same time independent of it’s original permissions on actual filesystem:
# touch /data/media/0/test_file # stat -c '%a %u %g %n' /data/media/0/test_file 644 1023 1023 /data/media/0/test_file # stat -c '%a %u %g %n' /mnt/runtime/*/emulated/0/test_file 660 0 1015 /mnt/runtime/default/emulated/0/test_file 640 0 9997 /mnt/runtime/read/emulated/0/test_file 660 0 9997 /mnt/runtime/write/emulated/0/test_file
Also see What is the “u#_everybody” UID?