Zygote
Builder tracknerd

N02 — Add a system service

Add a small service to system_server, register it with ServiceManager, and reach it through a Context manager object. The shortest path from reading about system services to having added one.

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

This project needs a completed AOSP build and a booting target. A system_server change is not something you can try in an app project: it needs the platform build, and each iteration is a build plus a boot. If you have not got that far, do N01 first — it also explains why the machine requirements are what they are.

TL;DR#

  • A system service is an object in system_server that other processes reach over Binder.
  • SystemService is the base class. It is started by SystemServiceManager, not by init.
  • ServiceManager.addService(name, binder) publishes the binder under a string name.
  • SystemServiceRegistry.registerService(...) is what makes context.getSystemService(...) return it.
  • The manager object in an app is a proxy. The service stays in system_server.

What you are building#

A tiny read-only service that stores a string and serves it back, plus a manager class an app can get from Context. The point is the wiring, not the feature: by the end you have followed one call from an app through Binder into system_server and back.

The shape of the change#

Four edits, in four different places, and they only work together:

WhereWhat you add
frameworks/base/services/core/java/com/android/server/The service implementation, extending SystemService
frameworks/base/services/java/com/android/server/SystemServer.javaA start call inside one of the start*Services methods
frameworks/base/core/java/android/app/SystemServiceRegistry.javaA registerService(...) entry
frameworks/base/core/java/android/content/Context.javaA name constant apps pass to getSystemService

The split is the lesson. The service is a binder; the registry is the app-facing lookup; the constant is the public name. AOSP keeps them separate because not every binder is meant to be a public API.1

Steps#

  1. Write the service class

    Create a file under frameworks/base/services/core/java/com/android/server/ — the directory that holds services such as BatteryService.java and Watchdog.java. Extend SystemService, implement onStart(), and publish your binder there.

    The illustrative shape:

    public class ToyService extends SystemService {
        private final IToyService.Stub mBinder = new IToyService.Stub() {
            @Override public String getValue() { return "hello from system_server"; }
        };
     
        public ToyService(Context context) { super(context); }
     
        @Override
        public void onStart() {
            publishBinderService("toy", mBinder);
        }
    }

    publishBinderService is the SystemService helper that ends up in ServiceManager. If you prefer the raw call, ServiceManager.addService(name, binder) does the same job.

    2
    You should seeA class that compiles against the framework and extends SystemService.
  2. Define the interface

    Add an .aidl file declaring the one method, and put it where both the framework and apps can see it. The generated Stub is what the service extends; the generated Proxy is what the app calls. The rules are the same as in I01 — primitives, strings, collections and parcelables only.

    You should seeA generated Stub the service and the client both compile against.
  3. Start it from SystemServer

    SystemServer.run() calls startBootstrapServices, startCoreServices, startOtherServices and startApexServices. Put your start call in the one that matches your service's dependencies, in the same style as the existing lines:

    mSystemServiceManager.startService(ToyService.class);

    Order matters. If your service reads settings or talks to the package manager, it must start after those services exist, which is exactly why there are several start phases and not one.

    You should seeThe service is constructed during boot.
  4. Register the app-facing lookup

    In SystemServiceRegistry.java, add a registerService entry. The private signature is:

    private static <T> void registerService(@NonNull String serviceName,
            @NonNull Class<T> serviceClass,
            @NonNull ServiceFetcher<T> serviceFetcher)

    AOSP's own window service entry shows the pattern:

    registerService(Context.WINDOW_SERVICE, WindowManager.class,
            new CachedServiceFetcher<WindowManager>() {
        @Override
        public WindowManager createService(ContextImpl ctx) {
            return new WindowManagerImpl(ctx);
        }});

    CachedServiceFetcher caches one instance per ContextImpl. Use StaticServiceFetcher when the service is not tied to a context.

    You should seegetSystemService returns your manager, not null.
  5. Add the name constant

    Add a public static final String constant to android.content.Context, next to the existing service names, and use it in both the registry entry and your client code. A bare string works, but a constant is how the platform keeps the name in one place.

    You should seeApps can refer to the service without a string literal.
  6. Rebuild and boot

    Bundle the framework and the services into the system image, then boot:

    source build/envsetup.sh
    lunch <your target>
    m

    The exact target string is the one you lunched in N01. Rebuilding one module first (m services or the module name from the relevant Android.bp) is much faster than m while you iterate. The AOSP build page documents the full build sequence and the resource it needs.

    3
    You should seeThe target boots and the service is alive.
  7. Prove it from adb
    adb shell service list | grep toy
    adb shell dumpsys toy

    service list enumerates registered binder services, so your name appearing there is proof that addService ran. dumpsys only produces output if your service implements the dump entry point; otherwise the empty result is expected.

    You should seedumpsys lists the service; the call returns your string.

Build and check#

Rebuild and inspect the service
source build/envsetup.sh
lunch <your target from N01>
m
adb shell service list | grep toy
adb shell dumpsys toy
Example output
Illustrative — no build was executed in this run.

<your target from N01> is a placeholder on purpose: the exact lunch target should be copied from the AOSP build page for your branch, not retyped from this page.

The files you touched#

Every path below can be read on Android Code Search without a local checkout, which is often the fastest way to check a signature before you edit it.4

Verification#

Three checks, in increasing strength:

  1. adb shell service list | grep <name> shows the binder.
  2. adb shell dumpsys <name> works if you implemented dumping.
  3. A client app calls the method and gets the value back, with the app's UID logged on the service side.

If step 1 fails, the service never started: check logcat for the constructor and for a SystemServiceManager failure. If step 1 passes and step 3 fails, the lookup is wrong — the registry entry or the permission, not the binder.

What can go wrong#

  • The service starts too early. It calls into a service that does not exist yet and throws during boot. Move the start call to a later phase.
  • getSystemService returns null. There is no registry entry, or the constant you passed does not match the one registered.
  • The build succeeds but the device does not boot. A system_server exception during startOtherServices is fatal-ish: the system restarts the service and you get a boot loop. Read logcat, not the build log.
  • SELinux denies the call. A new binder service needs a label in service_contexts and a domain allowed to find it.5 The denial appears as an avc: denied line in the kernel log.
  • A feature flag hides the code. Modern framework code is frequently gated by an aconfig flag; if your change seems to vanish, check whether the call site is behind one.6
  • You edited the wrong Context. There are several Context classes in the tree. The public one apps compile against is android.content.Context.

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. docsBinder overviewsource.android.comaccessed 2026-10-27↩
  3. docsBuild Androidsource.android.comaccessed 2026-10-27↩
  4. sourceAndroid Code Search (cs.android.com)cs.android.comaccessed 2026-10-27↩
  5. docsImplement SELinuxsource.android.comaccessed 2026-10-27↩
  6. docsConfiguration (aconfig) overviewsource.android.comaccessed 2026-10-27↩