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--user
flag to uninstall for all users.adb shell am get-current-user
gets the current (foreground) user ID.adb shell pm list users
gets a list of all existing users.adb shell pm create-user
creates a new user, returning the ID.adb shell pm remove-user
removes 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 (-e
for enabled,-d
for 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 theadbd
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. See Device paths for more details.If a default user isn't specified, each
adb
subcommand has a different user. The best practice is to retrieve the user ID witham get-current-user
and 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
/sdcard
paths 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:
adbd
is running as root (throughadb root
), which is only possible usinguserdebug
orusereng
builds.You're using Trade Federation's (Tradefed's)
ITestDevice
to push or pull the files, in which case use/sdcard/
paths in your test config (for example, see the source code forpushFile
inNativeDevice.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
ContentProvider
hosted by the app that can serve and store files where needed. Use the app's internal storage. - Use
adb shell content
read
orwrite
commands 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.