AOSP Framework & Internals
5 min read

Service Registration with ServiceManager

Learn about Service Registration with ServiceManager.

You have written a flawless AIDL interface and a concrete Java implementation. Your code works perfectly in isolation. The problem is that Android operates as a web of isolated sandboxes. No other process in the system knows your service exists or how to talk to it.

To solve this, Android uses a central registry called the servicemanager. This native daemon acts as the single source of truth for discovering system services. When your service starts, it must explicitly announce itself to this registry. It provides a unique string name and a reference to its underlying Binder object.

Other applications can then ask the servicemanager for that specific string name. The registry returns the Binder token required to establish a direct communication channel. Without this registration step, your service is just dead code running in a void.

Bridging the Gap with the SystemService Wrapper

The servicemanager daemon only maps strings to Binder tokens. It does not know how to construct your class, manage its memory, or integrate it into the Android boot sequence. You need a dedicated component to manage your service's lifecycle.

AOSP provides the SystemService base class for exactly this purpose. You rarely register a service directly from its own constructor. Instead, you create a wrapper class that extends SystemService to act as the bridge between your raw implementation and the OS boot process.

package com.android.server.mycustom;

import android.content.Context;
import com.android.server.SystemService;

public class MyCustomSystemService extends SystemService {
    private final MyCustomService mImpl;

    public MyCustomSystemService(Context context) {
        super(context);
        mImpl = new MyCustomService(context);
    }

    @Override
    public void onStart() {
        publishBinderService("my_custom_service", mImpl);
    }
}

This wrapper design separates the business logic of your service from the mechanics of booting it. The system will call the onStart() hook at the appropriate time. Inside that hook, you call publishBinderService() to hand your implementation over to the registry.

Wiring Your Service into SystemServer

Your wrapper class handles the registration logic, but it still needs to be instantiated. Android needs a central orchestrator to kick off all the various system components in the correct order.

This responsibility falls to SystemServer.java. The SystemServer process is the beating heart of the Android framework. It runs through specific boot phases to initialize everything from the power manager to the package manager.

To start your custom service, you add a single line to the appropriate phase in SystemServer.java. For non-critical services, this usually happens inside the startOtherServices() method.

// Inside SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    // ... existing system services ...

    t.traceBegin("StartMyCustomService");
    mSystemServiceManager.startService(MyCustomSystemService.class);
    t.traceEnd();

    // ...
}

When you call startService(), the system instantiates your wrapper and immediately executes its onStart() method. Your service is now alive and publicly accessible at the exact right moment in the boot sequence.

Tracing Registration Down to the Native Layer

Treating publishBinderService() as a black box is dangerous. If your service fails to register, you need to know exactly what happens at the IPC layer to debug it.

The following sequence diagram traces the registration path from the Java framework down to the native registry layer. This visualization clarifies the boundary between your Java implementation and the native system daemon. Pay attention to how the SystemServer orchestrates the flow while the actual registration happens across process boundaries.

Under the hood, the system casts your implementation to an IBinder object and initiates an IPC call to the /dev/binder driver. The kernel explicitly routes this transaction to process 0, which hosts the native servicemanager.

Before accepting the registration, the servicemanager validates the request against SELinux policies. It checks the service_contexts files to confirm that the system_server process has permission to add this specific service name. If the security check passes, the registry stores the Binder token in its internal hash map. This atomic check at the lowest level of the OS guarantees both security and reliable discovery.

Avoiding Boot Loops with Phase Callbacks

Booting a mobile operating system creates a massive dependency graph. Your new service might need to query the package manager to verify caller permissions. If you make that query inside your constructor or onStart() method, the device will likely crash. The package manager might not be fully initialized yet.

Common Mistake: Do not attempt to communicate with other system services inside your wrapper's constructor. Always wait for the appropriate boot phase callback to ensure the target service actually exists.

To manage these dependencies safely, the SystemService class provides the onBootPhase() callback. This mechanism allows your service to wait until specific milestones in the boot process are achieved.

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mImpl.systemReady();
        }
    }

The system guarantees that all core services are registered and fully functional when PHASE_SYSTEM_SERVICES_READY is reached. By deferring cross-service communication to a custom systemReady() method triggered by this phase, you avoid race conditions and boot loops. Your service respects the larger ecosystem it lives within.

We just traced the path of a custom service from isolated code to a globally discoverable system component. The servicemanager handles the native discovery and security checks. The SystemService wrapper manages the lifecycle and boot phases. Finally, SystemServer acts as the master orchestrator to tie everything together.

Your service is now registered, secure, and ready to accept calls. The next obvious problem is figuring out how client applications actually find and bind to this newly registered component from their own isolated processes.