Lab 8 — Intents
Use the shell to resolve intents, start components, and see the difference between explicit and implicit intents — including what happens when nothing matches and when several things do.
Intents are the component model's message format, and the shell is a good place to experiment with them: you can send one, see what resolves, and observe the result without writing a line of code.
TL;DR#
am startsends an intent to an activity.-nnames an explicit component;-a,-c,-dand-tbuild an implicit one.-Wwaits for the launch and prints timing.-Sforce-stops the app first.cmd package resolve-activityshows what an implicit intent would resolve to without starting anything.dumpsys packagelists the intent filters a package declares.- No match is a normal outcome, and the shell's error message is the fastest way to see it.
Mental model#
Think of a postal system with two addressing modes.
An explicit intent is a letter with a full address: a specific component in a specific package. The postal service delivers it or reports that the address does not exist.
An implicit intent is a letter addressed to "any shop that sells stamps". The postal service looks up who has registered for that description and picks one. If nobody registered, the letter is returned; if several did, the system may ask the sender to choose.
Where the analogy breaks#
A postal service has a fixed set of addresses. Android's registry is the installed packages, and it changes whenever an app is installed, disabled or updated. That is why an intent that resolved yesterday may not resolve today, and why "it works on my device" is a weak claim for implicit intents.
Deep dive#
The two addressing modes#
Explicit:
adb shell am start -n com.android.settings/.SettingsThe -n flag takes package/class. The class can be relative to the package (.Settings) or
fully qualified. An explicit intent does not consult intent filters; it names the component
directly.
Implicit:
adb shell am start -a android.intent.action.VIEW -d https://example.comThe -a flag is the action, -d the data URI. The system resolves the intent against every
installed package's filters and picks a match, or shows a chooser if there are several.
The flags you will use:
| Flag | Meaning |
|---|---|
-a | Action |
-c | Category |
-d | Data URI |
-t | MIME type |
-n | Explicit component |
-e | String extra |
-W | Wait for launch, print timing |
-S | Force-stop the target first |
Resolving without starting#
adb shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHERThis prints the component that would handle the intent. It is the safest way to explore resolution: nothing starts, nothing changes state.
The output shows the package and activity, or an error if nothing matches. For a chooser case, the result may name a resolver activity rather than a single target.
Reading filters#
adb shell dumpsys package com.android.settings | grep -A 10 "intent"Or, more precisely, look at the activities and their filters in the package dump. Each filter lists actions, categories and data types. That is the data the resolver matches against.
A filter with CATEGORY_DEFAULT is eligible for implicit intents; one without it is only reachable
explicitly. That single detail explains many "why can't this be opened?" questions.
The failure cases#
Nothing matches. The command returns an error naming the intent. This is normal: not every action has a handler.
Several match. The system may show a chooser, or resolve to a default if one is set. In the
shell, -W may report the resolver activity rather than the final target.
Permission denied. An activity can be exported but protected by a permission, in which case resolution succeeds and the start fails.
Broadcasts and services#
The same intent machinery starts services and sends broadcasts:
adb shell am startservice -n com.example.app/.SyncService
adb shell am broadcast -a android.intent.action.BOOT_COMPLETEDA broadcast to a protected action requires a permission. The shell has several, which is why some
broadcasts work from adb and not from an app.
Deep links and app links#
An intent with a data URI can launch an app directly if the app has verified the link. adb shell am start -a android.intent.action.VIEW -d <uri> is the standard way to test a deep link, and it
exercises the same resolution path a browser would.1
Walkthrough#
- Start an explicit activity
adb shell am start -W -n com.android.settings/.SettingsYou should seeThe activity starts; with -W, timing is printed. - Resolve the launcher intent
adb shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHERYou should seeA package/activity pair. - Open a URL
adb shell am start -a android.intent.action.VIEW -d https://example.comYou should seeA browser or a chooser. - Find a non-matching action
adb shell am start -a android.intent.action.NOT_A_REAL_ACTIONYou should seeAn error stating that no activity was found. - Read a package's filters
adb shell dumpsys package com.android.settings | grep -A 6 "android.intent.action.MAIN"}You should seeActivity entries with intent filters.
adb shell am start -W -n com.android.settings/.Settings
adb shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
adb shell am start -a android.intent.action.VIEW -d https://example.com
adb shell am start -a android.intent.action.NOT_A_REAL_ACTION
adb shell dumpsys package com.android.settings | grep -A 6 "android.intent.action.MAIN"Example output
Illustrative output — results depend on the device.Challenges#
Challenge 1 — Predict before you start#
Before running it, predict what am start -a android.intent.action.VIEW -d tel:12345 will do.
Reveal the answer
It resolves to the dialer (or the default handler for tel: URIs) and opens the dialer with the
number filled in. It does not place a call, because tel: is a dial action, not a call action. If
no dialer is installed, the start fails with no matching activity.
Challenge 2 — Find who handles a MIME type#
Determine which app would open a text/plain file.
Reveal the answer
adb shell cmd package resolve-activity --brief -a android.intent.action.VIEW -t text/plainIf several apps handle it, the result may be a chooser. To see the candidates, use
dumpsys package and look for text/plain in the filters, or query with the package manager's
intent resolution from an app.
Challenge 3 — Prove that filters beat guesses#
Pick an app and determine whether its main activity has CATEGORY_DEFAULT, and explain what that
means for implicit intents.
Reveal the answer
adb shell dumpsys package <package> | grep -B 2 -A 8 "android.intent.category.LAUNCHER"CATEGORY_LAUNCHER marks the launcher entry; CATEGORY_DEFAULT makes the filter eligible for
implicit intents. An activity with only LAUNCHER is reachable from the launcher and by explicit
intent, but an implicit intent without that category will not resolve to it. The two categories
answer different questions.
What you learned#
You can send, resolve and inspect intents from the shell, and you can explain a resolution failure. That is the component model from the outside, and it is the foundation for the tracing labs.
Next#
Lab 10 — Perfetto switches from state to time.
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsApp Linksdeveloper.android.comaccessed 2026-10-27↩
- docsAndroid Debug Bridge (adb)developer.android.comaccessed 2026-10-27↩
- docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩
- docsdumpsysdeveloper.android.comaccessed 2026-10-27↩
- docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩