Zygote
system_serverintermediate

The Package Manager

PackageManagerService is where an APK becomes an app: it parses the manifest, verifies the signature, assigns the UID, records the permissions, and answers every intent-resolution question afterwards.

12 minAPI 1–37 · 17interactivequizverified 2026-10-27

Ask an Android device a question about an app and you are almost always asking PackageManager. What is installed? What can handle this intent? Does this app hold the camera permission? Which signature signed it? All of those answers come from one service, and almost all of them were computed the first time the package was scanned.

TL;DR#

  • is the system's registry of installed software and the authority on what each package may do.
  • It scans at boot, parses every AndroidManifest.xml, verifies signatures and assigns UIDs.
  • It resolves intents by matching against filters parsed at install time, then filtered by the caller's package visibility.
  • It owns the permission grant state, though the permission definitions and policy moved to a separate service in Android 11.
  • Its state is the single biggest reason a first boot is slow, and the single biggest reason a corrupted package database produces a device that will not finish booting.

Mental model#

Think of a licensing office in a city.

When a business opens, it files a registration: its name, its address, what it intends to do, who owns it. The office checks the paperwork, records it, and issues an identifier. From then on, every question about that business — is it allowed to serve alcohol, can it operate at night, who is authorised to sign for it — is answered by looking it up in the register, not by visiting the shop.

PackageManager is that office. The APK is the filing. The register is the package database. The identifier is the UID.

Where the analogy breaks#

A licensing office is consulted by humans, occasionally. PackageManager is consulted by every intent resolution, every permission check, and every process start, thousands of times a minute. That is why its in-memory state is so aggressively precomputed and cached: the register has to answer in microseconds, not days.

The big picture#

Loading diagram…
Text description of this diagram

A timeline of system_server's boot phases. PackageManagerService starts early in the boot sequence, well before the phase at which the activity manager is ready, because almost every other service needs to know what is installed. Its scan is one of the dominant costs of a first boot. The phase constants are 100 for the default display, 200 for the sensor service, 480 for lock settings, 500 for system services, 520 for device-specific services, 550 for the activity manager, 600 for third-party apps, and 1000 for boot completed.

That placement is not arbitrary. PackageManager has to be ready before ActivityManager can launch anything, before the permission service can evaluate a grant, and before any app can be started. It is one of the first framework services up and one of the last to finish its background work.

Deep dive#

The scan#

At boot, PackageManagerService walks the directories that hold packages:

  • /system/apex/*/priv-app and /system/priv-app — privileged system apps.
  • /system/app — ordinary system apps.
  • /system_ext/..., /product/..., /vendor/... — the other partitions, each with its own policy.
  • /data/app — apps the user installed.

For each APK, it parses the binary AndroidManifest.xml, extracts the package name, version code, minSdkVersion, targetSdkVersion, components, permissions, shared libraries and split declarations, and verifies the signature. The results are held in an in-memory structure and persisted in a database under /data/system/.11

On a first boot this is slow because nothing is cached. On later boots the scan is largely incremental: known packages with unchanged timestamps and signatures are not re-parsed from scratch. That optimisation is why a device that has corrupted its package database takes minutes to boot instead of seconds — it is doing the first-boot path again.

Identity: UID, package name and signature#

Three identifiers are established at install time and never change for the life of the install.

The package name is the logical identity. It is unique per user, and it is what intents, permissions and the Play Store refer to.

The UID is the kernel identity. Android assigns a UID from a per-user range, which is why the same app has a different UID for each user on a multi-user device. The UID is the thing the kernel actually enforces, and it is why the sandbox survives even if the framework is confused.

The signature is the authorship identity. It is checked on every update: an update must be signed by the same key as the installed version, or it is refused. That single rule is what makes signature permissions meaningful and what makes app identity stable across updates.

Intent resolution#

When an app calls startActivity with an implicit intent, PackageManager has to answer "who can handle this". It does so by matching the intent against the filters it parsed at install time:

  • Action must match.
  • Category must match, with CATEGORY_DEFAULT implied for startActivity.
  • Data must match the scheme, host, port, path and MIME type, according to the filter's pattern.
  • The target must be exported and, for a given caller, visible.

The result is a resolution: a best match for explicit launches, or a list for a chooser.2

That visibility filter is one of the more confusing parts of modern Android, because the failure mode is silence: resolveActivity returns null and no exception is thrown. The package is installed, the filter is right, and the answer is still no.

Permissions, and the piece that moved#

PackageManager records which permissions a package declares and which it has been granted. That state is checked by ActivityManager on every IPC call and by the kernel's group IDs for things like network access.

Since API 30Android 11, the definitions and much of the policy live in a separate service: PermissionManagerService, published as "permission". The split separated two jobs that had grown together: knowing what an app is (package state) and deciding what it may do (permission policy).

is a third layer on top of both. It records per-operation allow/deny for things that are finer-grained than a permission — "may this app read the clipboard right now", "may it start a foreground service from the background". An app can hold a permission and still be blocked by an app-op, which is why "the permission is granted" is never the end of the story.

Installs#

An install is a conversation, not a file copy:

  1. The installer (Play Store, adb install, or a package installer app) streams the APK.
  2. PackageManager verifies the signature and the manifest, checks the version code against the installed one, and checks the requested permissions against policy.
  3. If the app requests a runtime permission, the grant may be deferred to a prompt.
  4. The APK is copied into /data/app/<package>-<random>/base.apk (the random suffix prevents path-guessing attacks).
  5. The package database is updated and the app's UID is assigned or reused.
  6. The package's components become resolvable.

Why it is a single point of failure#

The package database is one of the few pieces of state that the system cannot rebuild cheaply. If it is corrupted or lost, PackageManager rescans, which can take minutes and, on some devices, can leave the system unable to complete boot. That fragility is why the database is written carefully, why /data/system/packages.xml-era designs were replaced with a more robust store, and why "clear package cache" is a real recovery step rather than a placebo.

How it evolved#

Architecture changes29 releases
APIVersionWhat changed
11.0
  • Initial AOSP platform: Linux kernel, Dalvik VM, core libraries
  • The first zygote-based app spawning model
41.6 Donut
  • Support for different screen densities via resource qualifiers
52.0 Eclair
  • First multi-core-friendly scheduling and graphics work
82.2 Froyo
  • Trace-based JIT compiler added to Dalvik
  • Support for x86 and ARMv7 optimisations
92.3 Gingerbread
  • Concurrent garbage collector added to Dalvik (GC pause reduction)
113.0 Honeycomb
  • Hardware acceleration for the View system (opt-in per app)
133.2 Honeycomb
  • New screen-size resource qualifiers for compatibility scaling
144.0 Ice Cream Sandwich
  • One system image for phones and tablets
  • Hardware acceleration on by default
164.1 Jelly Bean
  • Project Butter: vsync, triple buffering, Choreographer
  • RenderThread architecture begins
174.2 Jelly Bean
  • Multi-user support on tablets
  • SELinux enforcement begins in the kernel (behind the scenes)
184.3 Jelly Bean
  • SELinux enforcement rolled out more broadly
  • Binder changes for restricted profiles
194.4 KitKat
  • Android Runtime (ART) appears as an experimental runtime
  • Kernel moved under a tighter vendor control
204.4W KitKat (Wear)
  • First time the platform shipped for a new form factor
215.0 Lollipop
  • ART becomes the only runtime: ahead-of-time compilation at install
  • 64-bit ARM and x86 support
  • RenderThread and OpenGL ES 3.1
  • Project Volta and JobScheduler
  • SELinux fully enforcing
225.1 Lollipop
  • Device protection ties verified boot to Google accounts
236.0 Marshmallow
  • Runtime permission model changes how every app asks for access
  • Doze batches background work
  • Fingerprint HAL
  • Adoptable storage
247.0 Nougat
  • Project Treble groundwork
  • A/B seamless updates
  • Hybrid JIT + AOT compilation in ART
  • MediaServer split into sandboxed processes
268.0 Oreo
  • Project Treble: the vendor interface separates vendor and system code
  • Background execution limits
  • Broadcast and service restrictions
  • HIDL for HALs
278.1 Oreo
  • Android Go: a lighter configuration of the same platform
289 Pie
  • App Standby Buckets
  • Mainline groundwork
  • Non-SDK interface restrictions (greylists) begin
  • BiometricPrompt
2910
  • Project Mainline: APEX and updatable system modules
  • Scoped storage
  • Bubbles
  • Project Mainline starts shipping through Play
3011
  • GKI: the Generic Kernel Image splits the kernel into core + vendor modules
  • App hibernation
  • Package visibility filtering
  • Incremental app installs
3112
  • Bluetooth permissions split into the nearby-devices family
  • Exact alarm permissions
  • Foreground service launch restrictions
3212L
  • Window size classes and activity embedding
3313 Tiramisu
  • Granular media permissions replace READ_EXTERNAL_STORAGE
  • The notification permission becomes a runtime permission
  • Predictive back gesture
3414 Upside Down Cake
  • Foreground service types are mandatory
  • Selected photos access via READ_MEDIA_VISUAL_USER_SELECTED
3515 Vanilla Ice Cream
  • 16 KB page size support for native libraries
  • Edge-to-edge is enforced when targeting 35
3616 Baklava
  • The release moves to Q2 (June) with a Q4 feature drop
  • Two SDK releases per year starting with 36
  • Desktop windowing arrives in developer options then stable
3717 Cinnamon Bun
  • Strict per-device app memory limits are enforced by the system
  • Generational garbage collection is added to ART's Concurrent Mark-Compact collector
  • A lock-free MessageQueue for apps targeting API 37
  • Compose-first: new platform and Jetpack APIs target Compose only; View libraries enter maintenance mode
  • Hybrid APK signing (v3.2) combines classical and ML-DSA post-quantum signatures
  • Native library dynamic code loading must be read-only

Three changes dominate the history. Runtime permissions (API 23Android 6.0) moved the grant decision from install time to run time, which turned PackageManager from a gate into a bookkeeper. Package visibility (API 30Android 11) made the candidate set a function of the caller, not of the device. Mainline moved pieces of the package and permission stack — MediaProvider, PermissionController — out of the platform entirely, so their behaviour can change without an OS release.

Under the hood#

Curiosity corner#

  • The UID range is per user. On a device with a work profile, the same app has two UIDs, one per user, and neither can read the other's data.
  • A package name can outlive its app. Uninstall does not free the name permanently; a reinstalled app with the same name and signature gets a fresh UID but the same identity.
  • There are packages with no code. A "stub" package with only a manifest and resources is legal and is how some system features ship an API surface without a process.

Try it#

Inspect a package

adb shell dumpsys package com.example.app | head -40 adb shell pm path com.example.app adb shell cmd package resolve-activity --brief -a android.intent.action.VIEW -d https://example.com

Example output
package:com.example.app
userId=10123
versionName=1.4.0
targetSdk=35
pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ]

The userId is the UID that the kernel uses. The pm path output shows every split belonging to the package. The resolve-activity call is PackageManager answering the exact question an implicit intent asks.

Check yourself#

Check yourself

1 / 12

How does system_server's process come into existence at boot?

0 answered · 0 correct

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 platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
  2. docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩
  3. docsPackage visibility filtering on Androiddeveloper.android.comaccessed 2026-10-27↩
  4. docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩
  5. docsPermissions on Android | Privacydeveloper.android.comaccessed 2026-10-27↩
  6. docsApplication sandboxsource.android.comaccessed 2026-10-27↩
  7. docsAbout Android App Bundlesdeveloper.android.comaccessed 2026-10-27↩