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.
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_serverthat other processes reach over Binder. SystemServiceis the base class. It is started bySystemServiceManager, not byinit.ServiceManager.addService(name, binder)publishes the binder under a string name.SystemServiceRegistry.registerService(...)is what makescontext.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:
| Where | What you add |
|---|---|
frameworks/base/services/core/java/com/android/server/ | The service implementation, extending SystemService |
frameworks/base/services/java/com/android/server/SystemServer.java | A start call inside one of the start*Services methods |
frameworks/base/core/java/android/app/SystemServiceRegistry.java | A registerService(...) entry |
frameworks/base/core/java/android/content/Context.java | A 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#
- Write the service class
Create a file under
frameworks/base/services/core/java/com/android/server/— the directory that holds services such asBatteryService.javaandWatchdog.java. ExtendSystemService, implementonStart(), 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); } }
2publishBinderServiceis theSystemServicehelper that ends up inServiceManager. If you prefer the raw call,ServiceManager.addService(name, binder)does the same job.You should seeA class that compiles against the framework and extends SystemService. - Define the interface
Add an
.aidlfile declaring the one method, and put it where both the framework and apps can see it. The generatedStubis what the service extends; the generatedProxyis 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. - Start it from SystemServer
SystemServer.run()callsstartBootstrapServices,startCoreServices,startOtherServicesandstartApexServices. 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. - Register the app-facing lookup
In
SystemServiceRegistry.java, add aregisterServiceentry. 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); }});CachedServiceFetchercaches one instance perContextImpl. UseStaticServiceFetcherwhen the service is not tied to a context.You should seegetSystemService returns your manager, not null. - Add the name constant
Add a
public static final Stringconstant toandroid.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. - Rebuild and boot
Bundle the framework and the services into the system image, then boot:
source build/envsetup.sh lunch <your target> mThe exact target string is the one you lunched in N01. Rebuilding one module first (
3m servicesor the module name from the relevantAndroid.bp) is much faster thanmwhile you iterate. The AOSP build page documents the full build sequence and the resource it needs.You should seeThe target boots and the service is alive. - Prove it from adb
adb shell service list | grep toy adb shell dumpsys toyservice listenumerates registered binder services, so your name appearing there is proof thataddServiceran.dumpsysonly 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#
source build/envsetup.sh
lunch <your target from N01>
m
adb shell service list | grep toy
adb shell dumpsys toyExample 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
- where the service implementation lives, next to BatteryService and Watchdog
- SystemService.javathe base class, including publishBinderService
- SystemServer.javathe four start phases and the startService calls
- SystemServiceRegistry.javathe name-to-fetcher table behind getSystemService
- Context.javathe public service name constants
Verification#
Three checks, in increasing strength:
adb shell service list | grep <name>shows the binder.adb shell dumpsys <name>works if you implemented dumping.- 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.
getSystemServicereturns 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_serverexception duringstartOtherServicesis 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_contextsand a domain allowed to find it.5 The denial appears as anavc: deniedline 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 severalContextclasses in the tree. The public one apps compile against isandroid.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.
- docsAndroid platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
- docsBinder overviewsource.android.comaccessed 2026-10-27↩
- docsBuild Androidsource.android.comaccessed 2026-10-27↩
- sourceAndroid Code Search (cs.android.com)cs.android.comaccessed 2026-10-27↩
- docsImplement SELinuxsource.android.comaccessed 2026-10-27↩
- docsConfiguration (aconfig) overviewsource.android.comaccessed 2026-10-27↩