Dissection of Android Services Internals

564

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_SERVCE) 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     * @paramname the name of the service to get
47     * @return a reference to the service, or <code>null</code> if the service doesn’t exist
48     */
49    publicstaticIBindergetService(Stringname) {
50        try {
51            IBinderservice =sCache.get(name);
52            if (service !=null) {
53                returnservice;
54            } else {
55                returngetIServiceManager().getService(name);
56            }
57        } catch (RemoteException e) {
58            Log.e(TAG, “error in getService”, e);
59        }
60        returnnull;
61    }


Suppose we don’t have the service in the cache. Hence we need to concentrate on the line 55

returngetIServiceManager().getService(name);


This call actually gets an 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


privatestaticIServiceManagergetIServiceManager() {
34        if (sServiceManager !=null) {
35            returnsServiceManager;
36        }
37
38        // Find the service manager
39        sServiceManager =ServiceManagerNative.asInterface(BinderInternal.getContextObject());
40        returnsServiceManager;
41    }


The ServicemanagerNative.asInterface() looks like the following:


/**
28     * Cast a Binder object into a service manager interface, generating
29     * a proxy if needed.
30     */
31    staticpublicIServiceManagerasInterface(IBinderobj)
32    {
33        if (obj ==null) {
34            returnnull;
35        }
36        IServiceManagerin =
37            (IServiceManager)obj.queryLocalInterface(descriptor);
38        if (in !=null) {
39            returnin;
40        }
41
42        returnnewServiceManagerProxy(obj);
43    }



So basically we are getting an 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


DECLARE_META_INTERFACE(ServiceManager) macro.


Its defined as


// ———————————————————————-
73
74#defineDECLARE_META_INTERFACE(INTERFACE)                               
75    staticconstandroid::String16descriptor;                          
76    staticandroid::sp<I##INTERFACE>asInterface(                       
77            constandroid::sp<android::IBinder>&obj);                  
78    virtualconstandroid::String16&getInterfaceDescriptor() const;    
79    I##INTERFACE();                                                     
80    virtual ~I##INTERFACE();                                            

And the IMPLEMENT_META_INTERFACE(ServiceManager, “android.os.IServiceManager”);

has been defined as follows:


#defineIMPLEMENT_META_INTERFACE(INTERFACE,NAME)                       
84    constandroid::String16 I##INTERFACE::descriptor(NAME);             
85    constandroid::String16&                                            
86            I##INTERFACE::getInterfaceDescriptor() const {              
87        return I##INTERFACE::descriptor;                                
88    }                                                                   
89    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                
90            constandroid::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 =newBp##INTERFACE(obj);                          
99            }                                                           
100        }                                                               
101        returnintr;                                                    
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:



  1. classIServiceManager :publicIInterface
    {
    public:
      
    staticconst android::String16 descriptor;  

  2.    static android::sp<IServiceManager> asInterface( const android::sp<android::IBinder>& obj);  

  3.    virtualconst android::String16& getInterfaceDescriptor() const;

  4.    IServicemanager();  

  5.    virtual ~IServiceManager();  

………

……..

……

…..


And in
IServiceManager.cpp


  1.  

  2.    const android::String16 IServiceManager::descriptor(“android.os.IServiceManager”);             

  3.    const android::String16&  

  4.           IServiceManager::getInterfaceDescriptor() const {  

  5.        return IServiceManager::descriptor;

  6.    }    

  7.    android::sp<IServiceManager> IServiceManager::asInterface(   

  8.            const android::sp<android::IBinder>& obj)  

  9.    {   

  10.        android::sp< IServiceManager> intr;    

  11.        if (obj != NULL) {     

  12.            intr = static_cast<IServiceManager*>(   

  13.                obj->queryLocalInterface(  

  14.                        IServiceManager::descriptor).get());    

  15.            if (intr == NULL) {   

  16.                intr = new BpServiceManager(obj);  

  17.            }  

  18.        }     

  19.        return intr;    

  20.    }     

  21.    IServiceManager::IServiceManager() { }    

  22.    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.


now once we get the reference of the Service Manager, we next call


publicIBindergetService(Stringname) throwsRemoteException {
116        Parceldata =Parcel.obtain();
117        Parcelreply =Parcel.obtain();
118        data.writeInterfaceToken(IServiceManager.descriptor);
119        data.writeString(name);
120        mRemote.transact(GET_SERVICE_TRANSACTION,data,reply, 0);
121        IBinderbinder =reply.readStrongBinder();
122        reply.recycle();
123        data.recycle();
124        returnbinder;
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:


publicbooleanonTransact(intcode,Parceldata,Parcelreply, intflags)
51    {
52        try {
53            switch (code) {
54            case IServiceManager.GET_SERVICE_TRANSACTION: {
55                data.enforceInterface(IServiceManager.descriptor);
56                String name =data.readString();
57                IBinderservice =getService(name);
58                reply.writeStrongBinder(service);
59                returntrue;
60            }
61
62            caseIServiceManager.CHECK_SERVICE_TRANSACTION: {
63                data.enforceInterface(IServiceManager.descriptor);
64                Stringname =data.readString();
65                IBinderservice =checkService(name);
66                reply.writeStrongBinder(service);
67                returntrue;
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.cpp

looks like the following:


 virtualsp<IBinder>getService(constString16&name) const
134    {
135        unsigned n;
136        for (n = 0; n < 5; n++){
137            sp<IBinder>svc =checkService(name);
138            if (svc !=NULL) returnsvc;
139            LOGI(“Waiting for service %s…n”,String8(name).string());
140            sleep(1);
141        }
142        returnNULL;
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 its 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.