Zygote
Hands-on labsintermediate

Lab 3 — Packages

Interrogate the package manager: list installed packages, read a manifest through dumpsys, check where an APK physically lives, and use pm to disable and re-enable a package safely.

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

The package manager knows what is installed, what each package may do, and where its files are. Almost every question that starts "why does this app…" can be answered by asking it. This lab is about asking well.

TL;DR#

  • pm list packages filters by kind: -3 third-party, -s system, -e enabled, -d disabled.
  • dumpsys package <name> prints the parsed manifest, permissions, signatures, UID and install timestamps.
  • pm path <name> gives the APK path. An app can have several split APKs.
  • pm disable-user disables a package reversibly; pm enable undoes it.
  • The APK path is under /data/app for installed apps and under /system (or another system partition) for system apps.

Mental model#

Think of a library's card catalogue.

Each card is the parsed manifest: title (package name), author (signer), subject (permissions), and shelf (APK path). The cards are what the librarian uses; the books themselves are just files.

The shelf list is pm list packages. It is an index, not the books.

The restricted section is the system partition: books that came with the building and cannot be removed without rebuilding it.

Where the analogy breaks#

A library's catalogue describes books that exist independently. Android's package manager is the authority: a package is installed because the package manager recorded it, and if the record is removed the app is gone even if the APK file remains. The database is the truth, not the file.

Deep dive#

Listing#

adb shell pm list packages

The basic form prints package:<name> lines. The filters matter:

FlagMeaning
-3Third-party (installed by the user)
-sSystem packages
-eEnabled
-dDisabled
-fInclude the APK path
-iInclude the installer

pm list packages -3 is the fastest way to see what the user installed; -s is the system image. -f turns the list into a map of where each APK lives.

Reading a package#

adb shell dumpsys package com.android.settings

The output is large. The sections worth knowing:

  • userId= — the app's UID.
  • versionCode / versionName — the version.
  • firstInstallTime / lastUpdateTime — install history.
  • signatures — the signing certificate.
  • requested permissions and install permissions — what it asked for and what it got.
  • pkgFlags — flags such as SYSTEM and PRIVILEGED.

The distinction between requested and granted permissions is the one most worth internalising: a manifest can ask for anything, and the platform decides what is actually granted.

Where the APK is#

adb shell pm path com.android.settings

For a user-installed app the path is under /data/app/...; for a system app it is under /system/ or another read-only partition. The path is useful for pulling the APK and inspecting it, and for understanding why an app survives a factory reset (system apps) or does not (user apps).

Changing state safely#

pm can change what is installed and enabled:

adb shell pm disable-user --user 0 com.example.app
adb shell pm enable com.example.app

disable-user marks the package disabled for a user without uninstalling it. It is reversible, and it is what the Settings UI does when you "disable" a preinstalled app.

Do not disable packages you depend on. Disabling the launcher, SystemUI or the package installer can leave a device in a state that is hard to recover without a reset. The lab uses a harmless package or none at all.

Package visibility#

Since Android 11, an app cannot see every installed package by default. Queries are filtered unless the app declares what it needs in its manifest, or holds a permission that grants visibility. The shell user is not subject to the same filter, which is why pm list packages shows everything while an app's PackageManager may not.1

That asymmetry is a common source of confusion when debugging: "the app can't see the launcher" is often a visibility declaration problem, not an install problem.

Walkthrough#

  1. Count third-party apps

    adb shell pm list packages -3 | wc -l

    You should seeA number, usually much smaller than the total.
  2. Find the Settings package with its path

    adb shell pm list packages -f | grep settings

    You should seeA line with the package name and an APK path.
  3. Read its UID and version

    adb shell dumpsys package com.android.settings | grep -E "userId|versionCode|versionName"

    You should seeuserId, versionCode and versionName lines.
  4. Read its permissions

    adb shell dumpsys package com.android.settings | grep -A 5 "requested permissions"

    You should seeSections for requested and install permissions.
  5. Find the split APKs

    adb shell pm path com.android.settings}

    You should seeOne or more apk paths.
Package interrogation
adb shell pm list packages -3 | wc -l
adb shell pm list packages -f | grep settings
adb shell dumpsys package com.android.settings | grep -E "userId|versionCode|versionName"
adb shell dumpsys package com.android.settings | grep -A 5 "requested permissions"
adb shell pm path com.android.settings
Example output
Illustrative output — package names and counts differ per device.

Challenges#

Challenge 1 — Distinguish a system app from a user app#

Pick two packages and determine which is on the system image.

Reveal the answer
adb shell pm path <package>
adb shell dumpsys package <package> | grep pkgFlags

A path under /system, /product or /vendor means a system app; a path under /data/app means a user app. The pkgFlags line may also include SYSTEM and PRIVILEGED. A privileged app is a system app that can hold permissions ordinary apps cannot.

Challenge 2 — Find an app with split APKs#

Find a package whose pm path output has more than one line.

Reveal the answer
adb shell pm list packages -3 | sed 's/package://' | while read p; do
  n=$(adb shell pm path "$p" | grep -c apk)
  [ "$n" -gt 1 ] && echo "$p $n"
done

Apps installed from an app bundle commonly have a base plus splits for ABI, screen density and language. The base contains the manifest and code; the splits contain resources and native libraries.

Challenge 3 — Disable and restore a package#

Disable a harmless package, confirm the state, then re-enable it.

Reveal the answer
adb shell pm disable-user --user 0 <package>
adb shell pm list packages -d | grep <package>
adb shell pm enable <package>
adb shell pm list packages -e | grep <package>

disable-user is reversible and is the same operation Settings performs. Choose a package you do not need; disabling a core system package can make the device difficult to use.

What you learned#

You can now answer "what is installed, what may it do, and where is it?" from the shell. That is the package manager's job, and it is also the starting point for permissions and intents.

Next#

Lab 4 — logcat shows how to read the log stream that the system writes while all of this runs.

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. docsPackage visibility filtering on Androiddeveloper.android.comaccessed 2026-10-27↩
  2. docsAndroid Debug Bridge (adb)developer.android.comaccessed 2026-10-27↩
  3. docsdumpsysdeveloper.android.comaccessed 2026-10-27↩
  4. docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩
  5. docsAbout Android App Bundlesdeveloper.android.comaccessed 2026-10-27↩
  6. docsAPK signature schemesource.android.comaccessed 2026-10-27↩