Test multiple users

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 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. 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 with am 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 (through adb root), which is only possible using userdebug or usereng 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 for pushFile in NativeDevice.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.

  1. Create an instance of ContentProvider hosted by the app that can serve and store files where needed. Use the app's internal storage.
  2. Use adb shell content read or write 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.