ByteScope

run-as

read-only

Runs a command as one app's own uid — the way to read a debug build's private files without root.

Command
adb shell run-as
Commands
System and logs
Risk
read-only

Syntax

run-as <package> <command> [<args>]

Flags and arguments

TokenMeaning
<package>The app to borrow the identity of. It has to be installed and marked debuggable in its manifest.
--user <id>Which Android user's copy of the package to become. Without it the helper uses the calling user, which over an ordinary cable is the primary one; pass it explicitly when the same package also exists inside a work profile.
<command>Everything after the package name is executed as the app, with no shell in between unless you ask for one. A redirection written on the same line is therefore performed by *your* shell under the `shell` uid, not by the app — which is exactly why writing the app's database out to shared storage works.

Examples

  • run-as com.example.debug id

    The probe: it prints the uid it actually became, so the answer is verifiable rather than merely silent.

  • run-as com.example.debug ls databases/

    List the app's SQLite files. The working directory is already the app's home.

  • run-as com.example.debug cat databases/app.db > /sdcard/app.db

    Copy a database somewhere you can pull it from.

  • run-as --user 0 com.example.debug id

    The same probe aimed at one named Android user, for a package that also exists inside a work profile.

  • run-as com.example.debug sh -c 'ls -l files cache'

    One shell running inside the app's identity, for when a single command is not enough. Quote the whole thing so your own shell hands it over untouched.

Reading the output

Whatever the command you ran prints. A refusal comes from `run-as` itself and says which of three things went wrong: the package is not debuggable, the package is unknown, or the helper is not on this build at all.

Output fields

FieldMeaning
uid=10234(u0_a234) …The `id` probe succeeded: you are the app now.
…is not debuggableA release build. Nothing to be done without root — this is the platform working.
unknown packageA typo, or the app is not installed for this user.
run-as: not foundThe build ships no helper. Rare, but it happens.
package not an applicationThe name is known to the platform but is not an installed app, so there is no uid to borrow.
run-as is disabled from the kernel commandlineThe helper was switched off when this image booted. A property of the build, not of the request.

What access it needs

Special, and worth reading before the failure lines make any sense. `/system/bin/run-as` is a setuid-root helper: it starts privileged, checks who is asking and what they asked for, then drops to the target app's uid and gid before executing your command. Three gates decide whether that happens, and reading the platform source they are unchanged from Android 11 through Android 15. The caller has to be `shell` or `root` — no other uid gets past the first check, which is why this cannot be smuggled into an app. The build must not have the helper disabled from the kernel command line. And the package has to be installed for the Android user you named and marked debuggable in its own manifest, which in practice means a debug build of something you compiled yourself. Having switched uid, it then re-validates the app's data directory before entering it: the path must be absolute, owned by that app's uid and gid, not readable or writable by others, and free of any `..` component. Any of those failing aborts before your command runs at all. So this needs no root, no `adb root` and nothing enabled in developer options — and equally it cannot be talked into a release build. A phone where you need that is a phone that needs root, and no flag on this page substitutes for it.

Failure messages, decoded

MessageMeaning
run-as: package not debuggable: com.exampleThe app's manifest does not declare itself debuggable. This is the platform working as designed rather than a defect: a store build is refused on every phone, rooted or not. Note the exact wording — Android 11 through 15 all print `package not debuggable`, while much older builds phrased the same refusal as a sentence about the package being not debuggable, so anything matching on the old phrasing will misfile a modern refusal as unrecognised.
run-as: unknown package: com.exampleThe package list held no such name. A typo, or the app is installed for a different Android user than the one being asked about — `pm list packages` settles which of the two it is, and it settles it from the platform rather than from memory.
run-as: only 'shell' or 'root' users can run this programThe calling uid is neither. Seen when the command is invoked from inside an app rather than from a cable; the helper checks its caller before it looks at the package at all, so this line tells you nothing about whether the package exists.
run-as: package not an application: com.exampleThe name resolved to something the platform knows about that is not an installed app, so there is no app uid available to become. Distinct from `unknown package`, and the distinction is the useful part.
run-as: run-as is disabled from the kernel commandlineThis build was booted with the helper switched off. Nothing about the app, the package name or your own uid changes the answer; it is a property of that boot, and the only route past it is a different image.
run-as: couldn't chdir to package's data directory '/data/user/0/com.example': Permission deniedThe uid switch succeeded and entering the app's home did not. Uncommon, and it usually means the data directory's ownership or mode failed one of the checks the helper performs immediately before this step — so read it as a statement about the directory on disk rather than about your access.

Version and vendor notes

  • This works only on an app whose manifest declares it debuggable, which in practice means a debug build of your own app. A Play Store app is refused, and that refusal is the security model rather than a bug.
  • The command starts in the app's home directory, so use relative paths like `databases/app.db`. There is no way to point it at another app's directory.
  • Copying a database while the app has it open can capture a half-written state. Stop the app first if the contents matter.
  • The gates are checked in a fixed order — the calling uid, then the kernel-command-line switch, then the package itself — so the first line that comes back tells you which one you are standing at, and there is no point arguing with a later one until the earlier one passes.
  • After switching uid the helper re-validates the app's data directory: absolute, correctly owned, not group- or world-accessible, and containing no `..`. A refusal at that stage describes the directory on disk rather than your own access, and it aborts before your command runs.

We have a UI for this

Android ADB Toolbox runs this command over USB and parses what comes back — a Chromium browser, no SDK, nothing uploaded.

Open Android ADB Toolbox

Related commands

Commands that turn up in the same session as run-as.

CommandWhat it does
logcatStreams the system log: the running commentary of everything happening on the phone.
dumpsys dropboxThe platform's ring buffer of crashes, ANRs and tombstones — how you read ANR traces without root.
pm pathPrints the absolute paths of the APK files that make up an installed package.

More System and logs commands

CommandWhat it does
topA one-shot process table: CPU share, memory, state and command line.
getpropPrints the system properties — the phone's own answer to what it is.

Checked against AOSP, toybox and kernel sources while writing the parsers behind our Web ADB tools. Where a behaviour genuinely varies by Android version or manufacturer, this page says so instead of giving a number that would be wrong on half the phones out there.

Frequently asked questions

How do I run an adb shell command?

Switch on developer options and USB debugging on the phone, plug it in, then run adb shell <command> from a machine with platform-tools installed — or run adb shell on its own to get an interactive prompt and type the command without the prefix. If the command produces binary output, such as a screenshot, use adb exec-out instead of adb shell: the plain shell service attaches a terminal, which rewrites newline bytes and corrupts the data.

Do adb shell commands need root?

Not the ones on this reference. They run as the shell user, which can manage packages for its own Android user, read most dumpsys services, write the settings provider, inject input and read shared storage. Root is only needed for things this reference deliberately avoids — reading another app's private data (except through run-as on a debuggable build), writing system partitions, or reading /data/anr directly.

Why does the output look different on my phone?

Either the Android version or the manufacturer. Some flags and fields arrived in a specific release, and those are noted per command. The rest is OEM patching, which hits dumpsys hardest: a dump is debug output whose text is whatever the service author last printed, and manufacturers change it and backport their changes. Read fields by name rather than by position, and treat an unfamiliar shape as a difference rather than as a failure.

Can I run this from a browser instead of a terminal?

For many commands, yes — the Web ADB tools on this site talk to a phone over WebUSB from a Chromium-based desktop browser, with no SDK installed and nothing uploaded. Each page links to the tool that runs its command, when one exists. Two limits are worth knowing: only one program can hold the ADB interface at a time, so a desktop adb server has to be stopped first, and adb forward and adb reverse cannot work in a browser at all.

Would rather click than type? The Web ADB Toolkit runs commands like this from a browser over USB. Or browse every command.