How does getSystemService() work exactly? How does getSystemService() work exactly? android android

How does getSystemService() work exactly?


See if my discussion makes sense...

dissection of android service internal

As suggested by one of the readers I am trying to copy some portion of the write-up here.

Have you ever wondered how an app gets an handle to the system services like POWER MANAGER or ACTIVITY MANAGER or LOCATION MANAGER and several others like these. To know that I dug into the source code of Android and found out how this is done internally.So let me start from the application side’s java code.

At the application side we have to call the function getService and pass the ID of the system service (say POWER_SERVICE) to get an handle to the service.

Here is the code for getService defined in /frameworks/base/core/java/android/os/ServiceManager.java

    /**44     * Returns a reference to a service with the given name.45     *46     * @param name the name of the service to get47     * @return a reference to the service, or <code>null</code> if the service doesn't exist48     */49    public static IBinder getService(String name) {50        try {51            IBinder service = sCache.get(name);52            if (service != null) {53                return service;54            } else {55                return getIServiceManager().getService(name);56            }57        } catch (RemoteException e) {58            Log.e(TAG, "error in getService", e);59        }60        return null;61    }

Suppose we don’t have the service in the cache. Hence we need to concentrate on line 55 return getIServiceManager().getService(name);

This call actually gets a handle to the service manager and asks it to return a reference of the service whose name we have passed as a parameter.

Now let us see how the getIServiceManager() function returns a handle to the ServiceManager.

Here is the code of getIserviceManager() from /frameworks/base/core/java/android/os/ServiceManager.java

private static IServiceManager getIServiceManager() {34        if (sServiceManager != null) {35            return sServiceManager;36        }3738        // Find the service manager39        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());40        return sServiceManager;41    }

The ServicemanagerNative.asInterface() looks like the following:

/**28     * Cast a Binder object into a service manager interface, generating29     * a proxy if needed.30     */31    static public IServiceManager asInterface(IBinder obj)32    {33        if (obj == null) {34            return null;35        }36        IServiceManager in =37            (IServiceManager)obj.queryLocalInterface(descriptor);38        if (in != null) {39            return in;40        }4142        return new ServiceManagerProxy(obj);43    }

So basically we are getting a handle to the native servicemanager.

This asInterface function is actually buried inside the two macros DECLARE_META_INTERFACE(ServiceManager) and IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");defined in IserviceManager.h and IServiceManager.cpp respectively.

Lets delve into the two macros defined in /frameworks/base/include/binder/IInterface.h

The DECLARE_META_INTERFACE(ServiceManager) macro is defined as

// ----------------------------------------------------------------------7374#define DECLARE_META_INTERFACE(INTERFACE)                               \75    static const android::String16 descriptor;                          \76    static android::sp<I##INTERFACE> asInterface(                       \77            const android::sp<android::IBinder>& obj);                  \78    virtual const android::String16& getInterfaceDescriptor() const;    \79    I##INTERFACE();                                                     \80    virtual ~I##INTERFACE();                                            \

And the IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); has been defined as follows:

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \84    const android::String16 I##INTERFACE::descriptor(NAME);             \85    const android::String16&                                            \86            I##INTERFACE::getInterfaceDescriptor() const {              \87        return I##INTERFACE::descriptor;                                \88    }                                                                   \89    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \90            const android::sp<android::IBinder>& obj)                   \91    {                                                                   \92        android::sp<I##INTERFACE> intr;                                 \93        if (obj != NULL) {                                              \94            intr = static_cast<I##INTERFACE*>(                          \95                obj->queryLocalInterface(                               \96                        I##INTERFACE::descriptor).get());               \97            if (intr == NULL) {                                         \98                intr = new Bp##INTERFACE(obj);                          \99            }                                                           \100        }                                                               \101        return intr;                                                    \102    }                                                                   \103    I##INTERFACE::I##INTERFACE() { }                                    \104    I##INTERFACE::~I##INTERFACE() { }

So if we replace expand these two macros in IServiceManager.h & IServiceManager.cpp file with the appropriate replacement parameters they look like the following:

class IServiceManager : public IInterface{public:   static const android::String16 descriptor;      static android::sp<IServiceManager> asInterface( const android::sp<android::IBinder>& obj);      virtual const android::String16& getInterfaceDescriptor() const;     IServicemanager();      virtual ~IServiceManager();  …......….....…...…..

And in IServiceManager.cpp

const android::String16 IServiceManager::descriptor("android.os.IServiceManager”);             const android::String16&         IServiceManager::getInterfaceDescriptor() const {      return  IServiceManager::descriptor;}    android::sp<IServiceManager> IServiceManager::asInterface(           const android::sp<android::IBinder>& obj)  {       android::sp< IServiceManager> intr;        if (obj != NULL) {             intr = static_cast<IServiceManager*>(               obj->queryLocalInterface(                      IServiceManager::descriptor).get());            if (intr == NULL) {               intr = new BpServiceManager(obj);          }      }         return intr;    }     IServiceManager::IServiceManager() { }    IServiceManager::~IIServiceManager { } 

So if you see the line 12 which shows if the Service Manager is up and running (and it should because the service manager starts in the init process during Android boot up) it returns the reference to it through the queryLocalinterface function and it goes up all the way to the java interface.

public IBinder getService(String name) throws RemoteException {116        Parcel data = Parcel.obtain();117        Parcel reply = Parcel.obtain();118        data.writeInterfaceToken(IServiceManager.descriptor);119        data.writeString(name);120        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);121        IBinder binder = reply.readStrongBinder();122        reply.recycle();123        data.recycle();124        return binder;125    }

from ServiceManagerNative.java. In this function we pass the service that we are looking for.

And the onTransact function for GET_SERVICE_TRANSACTION on the remote stub looks like the following:

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)51    {52        try {53            switch (code) {54            case IServiceManager.GET_SERVICE_TRANSACTION: {55                data.enforceInterface(IServiceManager.descriptor);56                String name = data.readString();57                IBinder service = getService(name);58                reply.writeStrongBinder(service);59                return true;60            }6162            case IServiceManager.CHECK_SERVICE_TRANSACTION: {63                data.enforceInterface(IServiceManager.descriptor);64                String name = data.readString();65                IBinder service = checkService(name);66                reply.writeStrongBinder(service);67                return true;68            }69//Rest has been discarded for brevity…………………..………………….………………….…………………

It returns the reference to the needed service through the function getService.The getService function from /frameworks/base/libs/binder/IServiceManager.cpplooks like the following:

  virtual sp<IBinder> getService(const String16& name) const134    {135        unsigned n;136        for (n = 0; n < 5; n++){137            sp<IBinder> svc = checkService(name);138            if (svc != NULL) return svc;139            LOGI("Waiting for service %s...\n", String8(name).string());140            sleep(1);141        }142        return NULL;143    }

So it actually checks if the Service is available and then returns a reference to it. Here I would like to add that when we return a reference to an IBinder object, unlike other data types it does not get copied in the client’s address space, but it's actually the same reference of the IBinder object which is shared to the client through a special technique called object mapping in the Binder driver.

To add more details to the discussion, let me go a little deeper into it.

The checkService function looks like the following:

virtual sp<IBinder> checkService( const String16& name) const    {        Parcel data, reply;        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());        data.writeString16(name);        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);        return reply.readStrongBinder();    }

So it actually calls a remote service and pass CHECK_SERVICE_TRANSACTION code (its an enum value of 2) to it.

This remote service is actually implemented in frameworks/base/cmds/servicemanager/service_manager.cand its onTransact looks like the following.

switch(txn->code) {   case SVC_MGR_GET_SERVICE:           case SVC_MGR_CHECK_SERVICE:        s = bio_get_string16(msg, &len);        ptr = do_find_service(bs, s, len);        if (!ptr)            break;        bio_put_ref(reply, ptr);        return 0;

Hence we end up calling the function named do_find_service which gets a reference to the service and returns it back.

The do_find_service from the same file looks as follows:

void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len){    struct svcinfo *si;    si = find_svc(s, len);//    ALOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);    if (si && si->ptr) {        return si->ptr;    } else {        return 0;    }

find_svc looks as follows:

struct svcinfo *find_svc(uint16_t *s16, unsigned len){    struct svcinfo *si;    for (si = svclist; si; si = si->next) {        if ((len == si->len) &&            !memcmp(s16, si->name, len * sizeof(uint16_t))) {            return si;        }    }    return 0;}

As it becomes clear that it traverses through the svclist and returns the the service we are looking for.


but I have trouble understanding what happens when calling ServiceManager.getService(LOCATION_SERVICE); even after reading the ServiceManager code.

Ok, so here's the source code of getService() in ServiceManager.java:

public static IBinder getService(String name) {    try {        IBinder service = sCache.get(name);        if (service != null) {            return service;        } else {            return getIServiceManager().getService(name);        }    } catch (RemoteException e) {        Log.e(TAG, "error in getService", e);    }    return null;}

As we can see, if the requested service is not cached yet, this calls getIServiceManager().getService(name). getIServiceManager() is a method in the same class(we will het to getService(name) in the next step):

private static IServiceManager getIServiceManager() {    if (sServiceManager != null) {        return sServiceManager;    }    // Find the service manager    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());    return sServiceManager;}

So this basically sends us to ServiceManagerNative.java where we need to look for getService(name):

public IBinder getService(String name) throws RemoteException {    Parcel data = Parcel.obtain();    Parcel reply = Parcel.obtain();    data.writeInterfaceToken(IServiceManager.descriptor);    data.writeString(name);    mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);    IBinder binder = reply.readStrongBinder();    reply.recycle();    data.recycle();    return binder;}

Which initiates a transaction to retrieve a service that has a name "LOCATION_SERVICE".

It's getting harder to move from here due to a complex structure of classes and interfaces that deal with the low-level stuff, like system services. But basically it's all done in Context.java, ContextImpl.java, ServiceManager.java and ServiceManagerNative.java. Also note that some of them might keep local caches or maps with references to service instances(i.e. you can see a sCache.get(name) in the ServiceManager.java listing above), this is where extra references may be coming from.

I don't think you will get a more detailed answer here on StackOverflow, because it becomes very low-level. You might want to ask somewhere like on an Android OS mailing list that has google employees on it.


The Location Manager, as most of the system services/managers is created in an early stage during boot process.

app_process is the native component which starts the DalvikVM, additionally, it tells ZigoteInit (The class which does the actual work) to launch the SystemServer. It's here where the first instance of LocationManager is created and where the reference is kept on the ServerThread within it.

/frameworks/base/services/java/com/android/server/SystemServer.javaDevicePolicyManagerService devicePolicy = null;StatusBarManagerService statusBar = null;InputMethodManagerService imm = null;AppWidgetService appWidget = null;NotificationManagerService notification = null;WallpaperManagerService wallpaper = null;-> LocationManagerService location = null;CountryDetectorService countryDetector = null;TextServicesManagerService tsms = null;LockSettingsService lockSettings = null;DreamManagerService dreamy = null;try {    Slog.i(TAG, "Location Manager");    location = new LocationManagerService(context);    ServiceManager.addService(Context.LOCATION_SERVICE, location);} catch (Throwable e) {    reportWtf("starting Location Manager", e);}

The rest is already know to you, i think.