WHY IS ADB DENIED ACCESS TO MULTI-USER STORAGE?
/storage/emulated/10 is denied from
adb shell due to this change in Android 9:
Add "default_normal" support to
This new flag isolates each user on a multi-user device for security reasons.
What the mount option
default_normal does in
sdcardfs is explained here:
default_normal option causes mounts with the gid set to
AID_SDCARD_RW to have user specific gids, as in the normal case.
The whole story is about Android's filesystem emulation in order to have a permission-less directory (
/sdcard) which makes file sharing among multiple UNIX users (apps) possible. It's achieved through mount namespaces and different VIEWs of
/data/media mounted in
/mnt/runtime/. Apps belonging to secondary as well as primary user have isolated mount namespaces. For details please see What is the “u#_everybody” UID? and What is /storage/emulated/0/?.
/sdcard/ always points to the first user's space
adb shell regardless what my current user is at mobile UI.
For all native processes running in root mount namespace (including
/sdcard is a symlink to
/storage/emulated is bind mounted from
/mnt/runtime/default. The device owner's (User_ID: 0) files in
/storage/emulated/0 have ownership
root:sdcard_rw (0:1015) and permission mode
0771, while secondary profile/user's (say with User_ID: 10) files in
/storage/emulated/10 have ownership
It means that non-root processes can read the directories only if they are members of supplementary groups:
11001015 and so on, "others" can only traverse the directories. Since
adbd is member of only
1015 GID, it can read only device owner's files, not of secondary users.
However up to Android 8 there was an exception to the above rule: to all the processes running in root mount namespace (non-app processes),
sdcardfs always returned the directories
/storage/emulated/[N] owned by GID
adb was able to read these directories. However the exception has been removed in Android 9 using mount option
HOW TO ACCESS MULTIPLE-USERS FILES FROM
WITH ROOT ACCESS:
You can mount the emulated filesystem without
~# umount /mnt/runtime/*/emulated
~# /system/bin/sdcard -u 1023 -g 1023 -m -w -G /data/media emulated
Or to make changes permanent, replace
sdcard binary with a shell script:
~# cd /system/bin/; mv sdcard sdcard.bin; touch sdcard
~# chown 0.2000 sdcard*; chmod 0755 sdcard*
~# chcon u:object_r:system_file:s0 sdcard
~# chcon u:object_r:sdcardd_exec:s0 sdcard.bin
-i argument passed by
set -- $(echo "$*" | sed 's/-i //')
After reboot you should be able to read files in
WITHOUT ROOT ACCESS:
On Android 9+ filesystem level access to secondary users/profiles is not possible from
user builds of ROMs (i.e. without root). Only interaction through
adb commands which support
--user option (
content etc.) is possible. Documented in What’s in Android 9 for enterprise apps:
"To help keep work data in the work profile, the Android Debug Bridge (adb) tool can’t access directories and files in the work profile."
If the files owned by secondary users are accessible to primary user (through
adb or any other means, except through Device/Work Policy Controller app), it breaks the intended isolation between users/profiles which is controlled through Android APIs. See more details in How to share files between regular account and work account?
It should be noted that Android Debugging Bridge (
adb) is meant to be used by developers for debugging, not by end users. That's why a fully-managed device owner can completely disable
There's a suggested workaround to transfer data with secondary users/profiles as explained in Testing Multiple Users:
adb (or more accurately the
adbd daemon) always runs as the system user (user ID = 0) regardless of which user is current. Therefore device paths that are user dependent (such as
/sdcard/) always resolve as the system user."
/sdcard paths of secondary users is denied starting in Android 9."
adb runs as the system user and data is sandboxed in Android 9 and higher, you must use content providers to push or pull any test data from a nonsystem user."
For instance to transfer
test.jpg file to
/storage/emulated/10/Pictures/ run the following commands from
~$ content insert --user 10 --uri content://media/external/images/media/ --bind _display_name:s:test.jpg
~$ ID=$(content query --user 10 --projection _id --uri content://media/external/images/media/ --where _display_name=\'test.jpg\' | grep -o '_id=[0-9]*' | cut -d= -f2)
~$ content write --user 10 --uri content://media/external/images/media/$ID < test.jpg
However it's not a practical approach for bulk data transfers on a regular basis.