adb pm and am commands: install, launch and manage Android apps
Most people learn adb install and stop there. Then they hit the wall: the app is installed but you can’t launch it without tapping the icon, you can’t tell if a permission was actually granted, and clearing app data means digging through Settings on a phone you’re trying to automate. That’s where pm (package manager) and am (activity manager) come in. They’re the two adb subcommands that let you manage the full lifecycle of an app from a terminal: install, inspect, launch, permission, clear, and remove, without ever touching the screen.
This is for anyone testing Android apps outside the Play Store flow: QA engineers running regression suites, developers side-loading debug builds, and anyone scripting device setup for a fleet of test phones. I’ll walk through the exact commands I use, the flags that matter, and what to check when a command silently does nothing (which pm and am do more often than you’d like).
I’m running these against Android 15 and platform-tools 35.0.2 (adb 1.0.41) on my own test devices. The commands below have been stable across Android versions for years, but I’ll flag anywhere behavior changed by API level.
what you need
- a computer with the Android SDK platform-tools installed (get it straight from developer.android.com rather than a random Play Store “adb” app, since those are unofficial forks)
- a USB cable or a wifi-debugging connection to a real device, or a running emulator, see emulator vs. real device testing if you’re deciding which to use for a given test
- USB debugging enabled in Developer Options, and the device authorized to your machine (first connection triggers an RSA key prompt, covered in adb authorization and RSA keys explained)
- the APK file you want to install, or the package name of an app already on the device
- for permission commands: knowledge of which permissions the app actually declares in its manifest (you can’t grant what isn’t declared)
- no cost beyond the device itself, platform-tools is free
step by step
1. confirm the device is connected and authorized
adb devices
Expected output:
List of devices attached
R3CR90XXXXX device
If it breaks: if the device shows unauthorized, unlock the phone and accept the RSA fingerprint prompt (walked through in adb authorization and RSA keys explained). If nothing shows up at all, check USB debugging is on, try a different cable or port, and run adb kill-server && adb start-server to reset the daemon.
2. find the package name
You need the exact package name (like com.example.myapp) for every command after this. If you don’t know it:
adb shell pm list packages -3
-3 filters to third-party (user-installed) apps only, which cuts out the hundred-plus system packages on a stock image. Add -f to see the APK path alongside each package, or pipe through grep to search:
adb shell pm list packages | grep whatsapp
Expected output: package:com.whatsapp
If it breaks: too many results with no filter usually means you forgot -3. If grep returns nothing, the app may be a system app, drop the -3 flag and search the full list.
3. install the APK
adb install -r -g /path/to/app.apk
-r: reinstall an existing app, keeping its data. Without this, installing over an already-present package fails.-g: grant all runtime permissions listed in the manifest at install time, useful for automated test setups where you don’t want a permission dialog blocking a script.-d: allow a version downgrade, needed if you’re installing an older build over a newer one.-t: allow install of test-only APKs (builds markedandroid:testOnly="true").
Expected output: Success
If it breaks: INSTALL_FAILED_ALREADY_EXISTS means you need -r. INSTALL_FAILED_VERSION_DOWNGRADE means add -d. INSTALL_FAILED_TEST_ONLY means add -t. These flags and their exact behavior are documented on Google’s adb reference page.
4. verify the install
adb shell pm path com.example.myapp
Expected output: package:/data/app/~~randomhash==/com.example.myapp-1/base.apk
If it breaks: pm path returns nothing or errors when the package name is wrong, double check the exact string from step 2, package names are case-sensitive.
5. find the launcher activity
To open an app by command, am start needs the full component name (package/activity), not just the package. On API 24 and above, resolve it directly:
adb shell cmd package resolve-activity --brief com.example.myapp
Expected output (the second line is what you want):
priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
com.example.myapp/.MainActivity
On older devices, or if that comes back empty, fall back to:
adb shell dumpsys package com.example.myapp | grep -A 1 "android.intent.action.MAIN"
Both approaches are looking for the activity tagged with the ACTION_MAIN intent action and CATEGORY_LAUNCHER category, the same pair the Intent documentation defines as what makes an activity show up as a launchable app icon.
If it breaks: some apps declare no launcher activity at all (libraries, background-only apps), in which case there’s nothing to launch this way, you’d start a service or broadcast instead.
6. launch the app
adb shell am start -n com.example.myapp/.MainActivity
Expected output: Starting: Intent { cmp=com.example.myapp/.MainActivity }
If it breaks: Error type 3\nError: Activity class {...} does not exist almost always means a typo in the component name or a missing leading dot, copy the exact string from step 5 rather than typing it from memory.
7. grant or revoke a specific permission
adb shell pm grant com.example.myapp android.permission.CAMERA
adb shell pm revoke com.example.myapp android.permission.CAMERA
Both commands are silent on success, no output means it worked. This only works for permissions with the “dangerous” protection level (the runtime permissions users get prompted for), which Google’s permissions guide covers in detail.
If it breaks: SecurityException: Permission ... is not a changeable permission type means the permission you named is a normal or signature permission, those are granted automatically at install and can’t be toggled with pm grant.
8. clear data, force-stop, or uninstall
Three different levels of “reset,” and it’s easy to reach for the wrong one:
adb shell pm clear com.example.myapp
adb shell am force-stop com.example.myapp
adb uninstall com.example.myapp
pm clear wipes all app data and cache, and resets the app to a fresh-install state, including revoking any permissions you granted in step 7. am force-stop just kills the running process and any background services, data stays intact. adb uninstall removes the app entirely.
If it breaks: pm clear on a protected system package returns Failed, that’s expected, most preinstalled system apps can’t be cleared this way without additional privileges.
common pitfalls
- forgetting
-ron a reinstall and gettingINSTALL_FAILED_ALREADY_EXISTS, then assuming the install command itself is broken - guessing the activity name instead of resolving it, wasting time trying
.MainActivity,.SplashActivity,.LauncherActivityin sequence whenresolve-activitywould have told you in one command - calling
pm granton a normal permission (likeINTERNET) and getting aSecurityException, then thinking the app or the device is broken - running
am startorpmcommands with more than one device connected and no-s <serial>flag, the command silently goes to whichever device adb picks first, not necessarily the one you meant - confusing
pm clearwitham force-stop, wiping test data you needed when all you wanted was to kill and relaunch the app
scaling this
At small scale, one or two devices plugged into your laptop, everything above works as-is. You just run the commands by hand or in a short shell script.
At 10 to 20 devices, USB hubs get unreliable and you start hitting the ambiguity problem from the pitfalls above. Every command needs a -s <serial> argument, or you switch to wifi debugging so devices aren’t fighting over USB bandwidth, which I cover in adb over wifi: wireless debugging. Wrap the steps in a loop over adb devices output so install and permission grants apply consistently across the batch instead of one device at a time.
At 100+ devices, physical wiring stops being practical and you’re managing a device lab or a farm, tracking which serials are online, retrying failed installs, logging am start results per device so you can see which ones didn’t actually launch. This is also where “own or are authorized to test” stops being a throwaway line, running automation against apps or accounts you don’t control crosses into territory most platforms’ terms explicitly prohibit.
If you need real Android hardware in Singapore rather than emulators or your own device rack, cloudf.one rents real Android phones on dedicated hardware there, each with its own persistent Singapore mobile IP, controlled from the browser. It’s Singapore-only, so it fits when the test needs a genuine Singapore-registered device rather than a spoofed locale. One limit worth knowing upfront: cloudf.one has no self-service adb, adb and Appium only reach a rented phone through a WireGuard tunnel that gets set up on request by emailing [email protected], so it’s not a drop-in replacement for a USB-connected test rig, it’s a different tool for a different problem.
where to go next
- if you’re managing devices visually as well as by command line, the scrcpy guide to controlling Android from your desktop covers mirroring and controlling a device from your desktop
- once
pmandamfeel routine, the natural next step is a real automation framework, compared in UiAutomator2 vs. Appium for Android automation - if you’re still deciding what to test against in the first place, emulator vs. real device testing lays out the tradeoffs
Browse more adb and Android testing guides at the blog.
Written by Xavier Fok
disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-09-13.