Friday, 24 May 2013

Adding New System Service in Android


To create a custom system service in android following steps are required

  1. Create service in framework
  2. Register service in SystemServer
  3. Create a .aidl interface of the service in framework
  4. Register .aidl interface in build system
  5. Create a manager for the service in framework
  6. Leverage the features of the service from service manager 
  7. Register the service manager in ContextImpl
  8. Register name of service manager in Context (abstract class)
After performing the above steps we need to build the new SDK for using the features of service through service manager.


1. Create service in framework

We need to create a service (Not an android service) in directory /frameworks/base/services/java/com/android/server/ where other system service are placed.

example code:

/*DemoService.java */
package com.android.server;
import android.content.Context;
import android.os.Handler;
import android.os.IDemoService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;

public class DemoService extends IDemoService.Stub {
    private static final String TAG = "DemoService";
    private DemoWorkerThread mWorker;
    private DemoWorkerHandler mHandler;
    private Context mContext;
    public DemoService(Context context) {
        super();
        mContext = context;
   mWorker = new DemoWorkerThread("DemoServiceWorker");
        mWorker.start();
        Log.i(TAG, "Spawned worker thread");
    }

    public void setData(int val) {
        Log.i(TAG, "setData " + val);
        Message msg = Message.obtain();
        msg.what = DemoWorkerHandler.MESSAGE_SET;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }

    private class DemoWorkerThread extends Thread {
        public DemoWorkerThread(String name) {
            super(name);
        }
        public void run() {
            Looper.prepare();
            mHandler = new DemoWorkerHandler();
            Looper.loop();
        }
    }

    private class DemoWorkerHandler extends Handler {
        private static final int MESSAGE_SET = 0;
        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
          Log.i(TAG,"set message received:"+msg.arg1);
                }
            } catch (Exception e) {
               // Log, don't crash!
          Log.e(TAG, "Exception in handleMessage");
            }
        }
    }
}


2. Register service in SystemServer
We need to register our service in /frameworks/base/services/java/com/android/server/SystemServer.javawhere other system service are registered.
/*SystemService.java*/
/*
 * go to function "@Override public void run()"
 * ........
 * Add following block after line "if (factoryTest !=
 * SystemServer.FACTORY_TEST_LOW_LEVEL) {" 
 */

try {
    Slog.i(TAG, "Starting Demo Service");
    ServiceManager.addService("demo", new DemoService(context));
    Slog.i(TAG, "Demo Service Started");
} catch (Throwable e) {
    Slog.e(TAG, "Failure starting DemoService Service", e);
}




3. Create a .aidl interface of the service in framework
A service can expose set of functions that can be access by other process/application, by esposing them in .aidl file.
To expose the functions of our DemoService we need to declare them in .aidl file in any existing directory or any new directory in framework 
Note: The directory represent the package structure so if we want to add our service manager in any existing package we have to put our .aidl in that directory other wise our new directory.
(here we will be creating our .aidl in framework's existing directory for simplicity, as creating new directory in framework is little complex which we will discuss later)
So we need to create IDemoService.aidl in direcotry 

/*
 * aidl file :
 * frameworks/base/core/java/android/os/IDemoService.aidl
 * This file contains definitions of functions which are
 * exposed by service.
 */
package android.os;
/**{@hide}*/
interface IDemoService {
        void setData(int val);
}


4. Register .aidl interface in build system

We need to register our service's aidl interface in frameworks/base/Android.mk
Note: here we don't have to add entry of any aidl files for parcelables if we use any in our IDemoService.aidl.

/*
 * open frameworks/base/Android.mk and add line in red 
 * color mentioned below which contains location of
 * our .aidl file.
 */
...
core/java/android/os/IPowerManager.aidl \
core/java/android/os/IDemoService.aidl \
core/java/android/os/IRemoteCallback.aidl \
...


5. Create a manager for the service in framework
There are two approaches to create the  our service manager either singleton or a normal class.
Note: If we create a normal class if we use that class from application we will end up with multiple instances of our service manager (still those different instances will contain same static instance of our system service), And than to provide single instance of our service manager we need to register our service manager in /frameworks/base/core/java/android/app/ContextImpl.java and 
so to optimized and simple we will see with singleton

Below is the sample code for a Singleton manager class:  

/**
* /framework/base/core/java/android/os/DemoProxy.java
* It will be available in framework through import android.os.DemoProxy;
*/
import android.os.IBinder;
import android.os.IDemoService;
import android.os.RemoteException;
import android.util.Log;
/**
 * Use this Singleton class to call the functionality of DemoService
 * @author shridutt.kothari
 * It is a like a ServiceFetcher for the ContextImpl.
 */
public class DemoProxy {
     private static final String TAG = "DemoProxy";
     private final IDemoService mDemoService;
     private static DemoProxy demoProxy;
 
     /** Get a handle to the DemoService.
      * @return the DemoService, or null.
      */
     public static synchronized DemoProxy getDemoProxy() {
            if(DemoProxy == null) {
            IBinder binder = android.os.ServiceManager.getService("demo");
                    if(binder != null) {
     IDemoService managerService = IDemoService.Stub.asInterface(binder);
                            demoProxy = new DemoProxy(managerService);
                    } else {
                           Log.e(TAG, "DemoService binder is null");
                    }
            }
            return demoProxy;
     }
 
     /**
      * Use {@link #getDemoProxy} to get the demoProxy instance.
      */
     DemoProxy(IDemoService demoService) {
          if(demoService == null){
            throw new IllegalArgumentException("demo service is null");
          }
          mDemoService = demoService;
     }
     /**
      * Sets the value in demoService 
      * @param arg
      */
     public void setData(int arg){
         try{
            Log.d(TAG, "Going to call service from framework proxy");
            mDemoService.setData(arg);
            Log.d(TAG, "Service called successfully from framework proxy");
         } catch (Exception e) {
            Log.d(TAG, "FAILED to call service from framework proxy");
            e.printStackTrace();
         }
     }
     /**
     * Get the binder of IDemoService.
     */
     public IDemoService getDemoService(){
            mDemoService;
     }
     
}
Now 3rd party apps can call getDemoService().setData(); method.

6. Leverage the features of the service from service manager 
We see in above code snippet setData (int arg) method is to leverage the feature of our custom service, As we have called mDemoService.setData(arg); inside setData method of our service manager (i.e. DemoProxy)


7. Register the service manager in ContextImpl


Now applications will be able to get the instance of our service manager with the DemoProxy.getDemoProxy(); But if our service manager is not a singleton class we need to register it in ContextImpl so that applications can get instance of service manger like Context.getSystemService("service_name");

Now our service, aidl of service, service manager all things are created, so we need to write following code in /frameworks/base/core/java/android/app/ContextImpl.java file in the static block: 

/**
 * In /frameworks/base/core/java/android/app/ContextImpl.java
 * inside the static block we need to register our ServiceFetcher like this:
 */
registerService(DEMO_SERVICE , new ServiceFetcher() {
        public Object createService(ContextImpl ctx) {
        return DemoProxy.getDemoProxy(); 
/**
 * Or we can create new object of it, if DemoProxy is not a singleton class.
 * see other registered Services for example.
 */
}});


8. Register name of the our service manager in Context (abstract class)

Now we need to add entry of DEMO_SERVICE as a constant in file


/** inside /frameworks/base/core/java/android/content/Context.java. */
/**
 * Use with {@link #getSystemService} to retrieve a
 * {@link android.os.DemoProxy} for using DemoService
 *
 * @see #getSystemService
 * @hide
 */
 public static final String DEMO_SERVICE = "demo";

After all the above steps we just need to build our custom 
sdk and than, we can use the service manager in applications like:


DemoProxy dm= (DemoProxy) getApplicatioContext().getSystemService(Context.DEMO_SERVICE);








2 comments:

  1. is this applicable to 8.1 as well ? do you need to update this for SELinux permissions ?

    ReplyDelete
  2. Lucky Club Casino Site - Live dealer casino with live dealers
    The luckyclub Lucky Club casino offers live betting on your favourite games and you can choose from a range of game types to suit your needs. The site also has a variety

    ReplyDelete