Zygote
system_serverintermediate

Managers and services: the pattern

Every framework feature is built the same way: a cheap client object you hold, a Binder proxy you never see, and a service in system_server that does the work. Learn the pattern once and a hundred APIs become predictable.

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

getSystemService(Context.CAMERA_SERVICE) returns a CameraManager. That object is not the camera service. It is not even a thin wrapper around a socket. It is a client-side object holding a Binder proxy, and every method you call on it is a remote call to a different process.

TL;DR#

  • The pattern is: Context constant → XxxManager (client) → Binder proxy → XxxService (server, usually in system_server).
  • SystemServiceRegistry maps constants to manager objects. It is a static block in the framework, not a lookup on the server.
  • The manager object is cheap to hold and is cached per Context. You rarely need to cache it yourself.
  • Binder calls are synchronous by default and run on the calling thread. A slow service call is a blocked UI thread.
  • Not every manager forwards to system_server: AudioManager reaches native code, NfcManager reaches an app process, and Mainline modules implement their own services.

Mental model#

Think of a company's switchboard numbers.

You keep a short list of extensions in your phone: 2100 for facilities, 2200 for IT, 2300 for accounts. Dialling 2100 does not put facilities in your pocket. It connects you to a desk in another building, and the conversation happens there.

The extension is the Context constant. The desk is the service. The phone is the manager object. And like a phone, the manager can be held while the desk is closed — dialling then just fails.

Where the analogy breaks#

A phone call is asynchronous: you leave a message. A Binder call is synchronous by default: you are on hold until the other side answers, and if the desk is slow, so are you. That difference is the single most important operational fact about the pattern, and it is why "don't call binder services on the main thread" is not folklore.

The big picture#

Loading diagram…
Text description of this diagram

A treemap of system_server's services grouped by area: activity and windows, packages and users, power and time, media and audio, connectivity, security and identity, and system and diagnostics. Each leaf is a registered Binder service. The point of the diagram in this context is that the manager you call corresponds to exactly one of these leaves, and the group tells you which part of the platform you are depending on.

Deep dive#

The four parts#

1. The constant. Context.ACTIVITY_SERVICE is a String, "activity". That is not a coincidence: the constant is the Binder name. Context.WINDOW_SERVICE is "window", and the names are public API, so they are effectively frozen.1

2. The manager. android.app.ActivityManager is a client class. It holds an IActivityManager proxy and implements convenience methods on top of it. The class is in framework.jar, which is why every app has it without shipping it.

3. The registry. SystemServiceRegistry is a static initialiser with one registerService(...) call per constant. Each call supplies a factory that builds the manager, and some also supply a "cached" variant that reuses the instance per process. That is why getSystemService normally returns the same object each time.

4. The service. ActivityManagerService is a Binder subclass published as "activity" in servicemanager. It implements IActivityManager.Stub.

What the call looks like on the wire#

// Client side, roughly what the generated proxy does.
public int getRecentTasks(int maxNum, int flags) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try {
        data.writeInterfaceToken(DESCRIPTOR);
        data.writeInt(maxNum);
        data.writeInt(flags);
        mRemote.transact(TRANSACTION_getRecentTasks, data, reply, 0);
        reply.readException();
        return reply.readInt();
    } finally {
        reply.recycle();
        data.recycle();
    }
}

Three things are worth noticing. The arguments are flattened into a ; there is no object graph crossing the boundary, only bytes. The call is blocking on transact unless the oneway flag is set. And readException() exists because an exception on the server has to be re-thrown on the client — Binder does not propagate Java exceptions natively, it marshals them.

Why the client is cheap#

A manager object holds a proxy, and a proxy holds a Binder handle — an integer. That is the entire client-side state. There is no connection to establish, no handshake, no buffer to allocate. The kernel looks up the handle in the caller's Binder context and routes the transaction to the owning process.

This is why the pattern scales to a hundred services.2 If each manager needed a socket and a connection thread, an app that touches ten services would pay ten times. Instead it pays a handle each.

The ones that do not fit#

The pattern is not universal, and the exceptions are instructive:

ManagerActually reachesWhy it is different
ActivityManagersystem_server, binder activityThe canonical case
PackageManagersystem_server, binder packageCanonical, but with a large local cache
AudioManagerNative AudioFlinger through AudioSystemMixing must not round-trip through a Java service
WifiManagerMainline Wi-Fi moduleThe service ships as a module, not in the platform
NfcManagerAn app process (com.android.nfc)NFC is implemented as an app with a privileged permission
SensorManagersystem_server facade over a native pollerThe service is thin; the data path is native

That last row explains a common confusion: SensorManager is in system_server, but the sensor samples do not travel over Binder. The service configures a native poller and, on modern Android, the data goes through shared memory (ASensorEventQueue with a BitTube), not one transaction per sample.

getSystemService returning null#

It can return null, and the reasons are worth knowing because they are all different:

  • The name is wrong. A typo in a string constant returns null silently. getSystemService takes a String, and there is no compile-time check that the string exists.
  • The service is not available on this device. Some services are optional or gated by hardware. A device without NFC has an NfcManager class but nothing to connect to.
  • The process is too early. A ContentProvider.onCreate or Application.attachBaseContext may run before the registry is fully populated for that context.
  • The service is restricted. Some services are unavailable to apps without a permission and return null instead of throwing, which is a deliberate choice to avoid making the restriction discoverable.

The catalogue#

Below is a cut of this site's service dataset: thirty of the most-used services, with the constant you pass, the manager you get back, the Binder name the service is published under, and the API level the manager class appeared. Filter it to find the one you are looking for.

Thirty framework services: the Context constant, the client manager, the Binder name and the process that hosts the service.
Context constantManager classBinder nameHost processSince
ACTIVITY_SERVICEandroid.app.ActivityManageractivitysystem_server1
ACTIVITY_TASK_SERVICEandroid.app.ActivityTaskManageractivity_tasksystem_server29
WINDOW_SERVICEandroid.view.WindowManagerwindowsystem_server1
PACKAGE_SERVICEandroid.content.pm.PackageManagerpackagesystem_server1
PERMISSION_SERVICEandroid.permission.PermissionManagerpermissionsystem_server30
APP_OPS_SERVICEandroid.app.AppOpsManagerappopssystem_server19
USER_SERVICEandroid.os.UserManagerusersystem_server17
POWER_SERVICEandroid.os.PowerManagerpowersystem_server1
ALARM_SERVICEandroid.app.AlarmManageralarmsystem_server1
JOBSCHEDULER_SERVICEandroid.app.job.JobSchedulerjobschedulersystem_server21
NOTIFICATION_SERVICEandroid.app.NotificationManagernotificationsystem_server1
INPUT_SERVICEandroid.hardware.input.InputManagerinputsystem_server16
DISPLAY_SERVICEandroid.hardware.display.DisplayManagerdisplaysystem_server17
INPUT_METHOD_SERVICEandroid.view.inputmethod.InputMethodManagerinput_methodsystem_server3
ACCESSIBILITY_SERVICEandroid.view.accessibility.AccessibilityManageraccessibilitysystem_server4
CAMERA_SERVICEandroid.hardware.camera2.CameraManagermedia.camerasystem_server21
AUDIO_SERVICEandroid.media.AudioManageraudionative1
MEDIA_SESSION_SERVICEandroid.media.session.MediaSessionManagermedia_sessionsystem_server21
MEDIA_PROJECTION_SERVICEandroid.media.projection.MediaProjectionManagermedia_projectionsystem_server21
CONNECTIVITY_SERVICEandroid.net.ConnectivityManagerconnectivitysystem_server1
WIFI_SERVICEandroid.net.wifi.WifiManagerwifimainline1
TELEPHONY_SERVICEandroid.telephony.TelephonyManagerphonesystem_server1
VPN_MANAGEMENT_SERVICEandroid.net.VpnManagervpn_managementsystem_server30
LOCATION_SERVICEandroid.location.LocationManagerlocationsystem_server1
SENSOR_SERVICEandroid.hardware.SensorManagersensorservicesystem_server1
KEYSTORE_SERVICEandroid.security.keystore.KeyStoreManagerandroid.security.keystore2system_server1
BIOMETRIC_SERVICEandroid.hardware.biometrics.BiometricManagerbiometricsystem_server29
STORAGE_SERVICEandroid.os.storage.StorageManagermountsystem_server1
CLIPBOARD_SERVICEandroid.content.ClipboardManagerclipboardsystem_server11
CREDENTIAL_SERVICEandroid.credentials.CredentialManagercredentialsystem_server34

Two columns repay study. The binder name column shows how often the name differs from the constant: STORAGE_SERVICE maps to "mount", TELEPHONY_SERVICE maps to "phone", SENSOR_SERVICE maps to "sensorservice". Those names predate the public constants and cannot be changed without breaking every native client.

The host process column shows that the pattern is about the client API, not about system_server specifically. A manager can front a native daemon or a module.

Writing your own#

The pattern is not reserved for the platform. A bound Service with an AIDL interface is the same shape at a smaller scale: the client holds a proxy, the service implements a Stub, and the arguments are parcelled. What you do not get is the SystemServiceRegistry shortcut, because that is registered by the framework, not by apps.

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

The pattern itself has not changed since 2008. What changes is where the server lives. Services have been split (ActivityTaskManager, PermissionManager), moved into Mainline modules (Wi-Fi, Bluetooth, MediaProvider), and occasionally moved out of system_server entirely (NFC). The client API has stayed stable through all of it, which is the entire point of the boundary.

Under the hood#

Curiosity corner#

  • The Binder name is not always the constant lowercased. STORAGE_SERVICE is "mount" because the service predates the constant and the name was already used by native clients.
  • getSystemService can return an object whose service is not running. The manager exists regardless; the failure happens on the first call, not on the lookup.
  • Context.getSystemService is cached per context, not per app. Two contexts in the same process may return two manager objects wrapping the same proxy.

Try it#

Enumerate the services and their names

adb shell service list | head -20 adb shell service list | grep -E "activity|package|window"

Example output
Found 218 services:
0	android.app.IActivityManager: []
1	android.view.IWindowManager: []
2	android.content.pm.IPackageManager: []

service list prints the Binder name and the interface descriptor. It is the ground truth for the pattern: if a name is not in that list, no amount of getSystemService will reach it.

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. docsContext | API referencedeveloper.android.comaccessed 2026-10-27↩
  2. docsBinder overviewsource.android.comaccessed 2026-10-27↩
  3. docsAndroid platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
  4. docsAIDL for HALssource.android.comaccessed 2026-10-27↩
  5. docsApplication sandboxsource.android.comaccessed 2026-10-27↩