This page describes important aspects of testing multiple users on the Android platform. For information about implementing multi-user support, see Support multiple users.
Device paths
The following table lists several of the device paths and how they are resolved. All values in the Path column are a user-specific sandboxed storage. Android's storage story has changed over time; read the Storage documentation for more information.
| Path | System path (optional) | Purpose |
|---|---|---|
/data/user/{userId}/{app.path}
|
/data/data
|
App storage |
/storage/emulated/{userId}
|
/sdcard
|
Shared internal storage |
/data/media/{userId}
|
none | User media data (for example, music, videos) |
/data/system/users/{userId}
|
none | System configuration/state per user
Accessible only by system apps |
Here's an example of using a user-specific path:
# to access user 10's private application data for app com.bar.foo:
$ adb shell ls /data/user/10/com.bar.foo/
adb interactions across users
Several adb commands are useful when dealing with multiple users. Some of
these commands are supported only in Android 9 and
higher:
adb shell am instrument --user <userId>runs an instrumentation test against a specific user. By default this uses the current user.adb install --user <userId>installs a package for a specific user. To ensure that a package is installed for all users, you must call this for every user.adb uninstall --user <userId>uninstalls a package for a specific user. Call without the--userflag to uninstall for all users.adb shell am get-current-usergets the current (foreground) user ID.adb shell pm list usersgets a list of all existing users.adb shell pm create-usercreates a new user, returning the ID.adb shell pm remove-userremoves a specific user by ID.adb shell pm disable --user <userId>disables a package for a specific user.adb shell pm enable --user <userId>enables a package for a specific user.adb shell pm list packages --user <userId>lists packages (-efor enabled,-dfor disabled) for a specific user. By default this always lists for the system user.
The following information helps explain how adb behaves with multiple users:
adb(or more accurately theadbddaemon) 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. See Device paths for more details.If a default user isn't specified, each
adbsubcommand has a different user. The best practice is to retrieve the user ID witham get-current-userand then explicitly use--user <userId>for any command that supports it. Explicit user flags weren't supported for all commands until Android 9.Access to
/sdcardpaths of secondary users is denied starting in Android 9. See Content provider for multi-user data for details on how to retrieve files during testing.
Content provider for multi-user data
Because 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. This is not necessary if:
adbdis running as root (throughadb root), which is only possible usinguserdebugoruserengbuilds.You're using Trade Federation's (Tradefed's)
ITestDeviceto push or pull the files, in which case use/sdcard/paths in your test config (for example, see the source code forpushFileinNativeDevice.java).
When a content provider is running in the secondary user, you can access it by
using the adb shell content command with the appropriate user, uri, and
other parameters specified.
Workaround for app developers
Interact with test files using adb content and an instance of
ContentProvider,
instead of the push or pull command.
- Create an instance of
ContentProviderhosted by the app that can serve and store files where needed. Use the app's internal storage. - Use
adb shell contentreadorwritecommands to push or pull the files.
Workaround for media files
To push media files to the media partition of the SD card, use MediaStore public
APIs. For example:
# push MVIMG_20190129_142956.jpg to /storage/emulated/10/Pictures
# step 1
$ adb shell content insert --user 10 --uri content://media/external/images/media/ --bind _display_name:s:foo.jpg
# step 2
$ adb shell content query --user 10 --projection _id --uri content://media/external/images/media/ --where "_display_name=\'foo.jpg\'"
# step 3
$ adb shell content write --user 10 --uri content://media/external/images/media/8022 < MVIMG_20190129_142956.jpg
Install a generic content provider
Install and use an existing content provider that reads and writes files to the
user-specific /sdcard path.
Build the TradefedContentProvider.apk from the source using
make TradefedContentProvider:
```
# install content provider apk
$ adb install --user 10 -g TradefedContentProvider.apk
# pull some_file.txt
$ adb shell content read --user 10 --uri content://android.tradefed.contentprovider/sdcard/some_file.txt > local_file.txt
# push local_file.txt
$ adb shell content write --user 10 --uri content://android.tradefed.contentprovider/sdcard/some_file.txt < local_file.txt
```
Trade Federation multi-user support
Tradefed is the official Android test harness. This section summarizes some of Tradefed's built-in support for multi-user test scenarios.
Status checkers
System status checkers (SSCs) are run before the target preparers, and their cleanup is run after those preparers.
UserChecker
is defined explicitly to aid developers when testing multiple users. It tracks
whether a test has changed the state of the users on the device (for example,
created users without removing them in teardown). In addition, if user-cleanup
is set, it automatically attempts to clean up after the test, while still
providing helpful errors so that the test can be fixed.
<system_checker class="com.android.tradefed.suite.checker.UserChecker" >
<option name="user-cleanup" value="true" />
</system_checker>
Target preparer
Target preparers are typically used to set up a device with a particular configuration. In the case of multi-user testing preparers can be used to create users of a specific type as well as switch to other users.
For device types that don't have a secondary user, you can use
CreateUserPreparer to create and switch to a secondary user in
AndroidTest.xml. At the end of the test, the preparer switches back and
deletes the secondary user.
<target_preparer
class="com.google.android.tradefed.targetprep.CreateUserPreparer" >
</target_preparer>
If the user type you want already exists on the device, use
SwitchUserTargetPreparer to switch to the existing user. Common values for
user-type include system or secondary.
<target_preparer
class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
<option name="user-type" value="secondary" />
</target_preparer>
Host-driven tests
In some instances, a test needs to switch users within the test. Don't
make the switch from within a device-side test framework, such as
UI Automator,
because the test process can be killed at any time. Instead, use a host-side
test framework like Tradefed's Host-driven test
framework,
which gives access to
ITestDevice,
allowing for any user manipulation that is needed.
Use UserChecker (described in
Status checkers) for host-driven tests that change
the user state because it ensures that the test properly cleans up after itself.