Zygote
Hands-on labsintermediate

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.

5 minAPI 1–37 · 17verified 2026-10-27

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 start sends an intent to an activity. -n names an explicit component; -a, -c, -d and -t build an implicit one.
  • -W waits for the launch and prints timing. -S force-stops the app first.
  • cmd package resolve-activity shows what an implicit intent would resolve to without starting anything.
  • dumpsys package lists 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/.Settings

The -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.com

The -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:

FlagMeaning
-aAction
-cCategory
-dData URI
-tMIME type
-nExplicit component
-eString extra
-WWait for launch, print timing
-SForce-stop the target first

Resolving without starting#

adb shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER

This 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_COMPLETED

A 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.

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#

  1. Start an explicit activity

    adb shell am start -W -n com.android.settings/.Settings

    You should seeThe activity starts; with -W, timing is printed.
  2. Resolve the launcher intent

    adb shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER

    You should seeA package/activity pair.
  3. Open a URL

    adb shell am start -a android.intent.action.VIEW -d https://example.com

    You should seeA browser or a chooser.
  4. Find a non-matching action

    adb shell am start -a android.intent.action.NOT_A_REAL_ACTION

    You should seeAn error stating that no activity was found.
  5. 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.
Intent experiments
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/plain

If 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.

  1. docsAndroid Debug Bridge (adb)developer.android.comaccessed 2026-10-27↩
  2. docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩
  3. docsdumpsysdeveloper.android.comaccessed 2026-10-27↩
  4. docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩