SoFunction
Updated on 2025-04-07

Detailed explanation of how to add system services on Android

1. Preface

System services are a very important part of Android, such as ActivityManagerService, PackageManagerService, and WindowManagerService. These system services are key services in the Framework layer. This article mainly talks about how to add a system service based on Android source code. In addition to adding basic system services, it also includes adding part of JNI code and a demo of the app calling through AIDL. The call includes the App to call the server and also the server callback App, which means completing a simple two-way communication.

Note: The test code is based on Android 7.1.1, and other Android versions are similar.

2. Write AIDL files

To add a service, the first thing is to write an AIDL file, and the AIDL file path is as follows:

frameworks/base/core/java/com/example/utils/

The content is as follows:

package ;

import ;

interface ISystemEvent {
  void registerCallback(IEventCallback callback);

  void unregisterCallback(IEventCallback callback);

  void sendEvent(int type, String value);
}

The content is as follows

package ;

interface IEventCallback
{
  oneway void onSystemEvent(int type, String value);
}

There are many tutorials for writing AIDL files, so I won't explain them in detail here. It should be noted that since we want to implement the callback function, we must write a callback interface IEventCallback. In addition, the oneway keyword in the AIDL file indicates that calling this function will not block the current thread. The calling side will return immediately when calling this function. The receiving side receives the function call in a thread in the Binder thread pool. You can choose whether to add the oneway keyword according to the actual project requirements.

AIDL only supports the transmission of basic Java type data. To pass custom classes, the class needs to implement the Parcelable interface. In addition, if the basic type array is passed, the in out keyword needs to be specified, such as void test(in byte[] input, out byte[] output) , use in or out, just remember: if the array is used as a parameter and is passed to the called end through the calling end, use in. If the array is only used to accept data, and the actual data is filled by the called end, then use out. The reason why the server and client are not mentioned here is because which of the in out keyword is used is the server or client, and remote calls and called are more suitable for description.

After the file is finished, add it to the compiled LOCAL_SRC_FILES after:

/base/

LOCAL_SRC_FILES += \
  core/java/android/view/ \
  core/java/android/view/ \
  core/java/android/view/ \
  Some codes are omitted ...
  core/java/com/example/utils/ \
  core/java/com/example/utils/ \
  Some codes are omitted ...

Compile the code. Before compiling, you need to execute make update-api, update the interface, and then compile the code to ensure that there are no errors in writing AIDL. After compilation, the corresponding java file will be generated. The server needs to implement the corresponding interface.

3. Write Manager class

We can see that there are many Manager classes in the Android API. These classes are usually client proxy classes for a certain system service. In fact, we do not write the Manager class, but only automatically generated classes through AIDL files can also complete the functions. However, it is more convenient to encapsulate the AIDL interface. The Manager class we used for testing is SystemEventManager, and the code is as follows:

frameworks/base/core/java/com/example/utils/

package ;

import ;
import ;
import ;

import ;
import ;

public class SystemEventManager {

  private static final String TAG = ();
  // The name used when registering the system service to ensure that it does not conflict with the existing service name  public static final String SERVICE = "test_systemevent";

  private final Context mContext;
  private final ISystemEvent mService;

  public SystemEventManager(Context context, ISystemEvent service) {
    mContext = context;
    mService = service;
    (TAG, "SystemEventManager init");
  }

  public void register(IEventCallback callback) {
    try {
      (callback);
    } catch (RemoteException e) {
      (TAG, "remote exception happen");
      ();
    }
  }

  public void unregister(IEventCallback callback) {
    try {
      (callback);
    } catch (RemoteException e) {
      (TAG, "remote exception happen");
      ();
    }
  }

  /**
   * Send event to SystemEventService.
   */
  public void sendEvent(int type, String value) {
    try {
      (type, value);
    } catch (RemoteException e) {
      (TAG, "remote exception happen");
      ();
    }
  }
}

The code is very simple, so it encapsulates the AIDL interface and defines the name used when registering the system service.

public SystemEventManager(Context context, ISystemEvent service)

The ISystemEvent parameter in the constructor will be obtained through the Binder-related interface when registering the Manager later.

Compile the code and make sure there are no errors. Write the system service below.

IV. Write system services

The path and code are as follows:
frameworks/base/services/core/java/com/android/server/example/

package ;

import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;

public class SystemEventService extends  {

  private static final String TAG = ();
  private RemoteCallbackList<IEventCallback> mCallbackList = new RemoteCallbackList<>();

  private Context mContext;

  public SystemEventService(Context context) {
    mContext = context;
    (TAG, "SystemEventService init");
  }

  @Override
  public void registerCallback(IEventCallback callback) {
    boolean result = (callback);
    (TAG, "register pid:" + ()
        + " uid:" + () + " result:" + result);

  }

  @Override
  public void unregisterCallback(IEventCallback callback) {
    boolean result = (callback);
    (TAG, "unregister pid:" + ()
        + " uid:" + () + " result:" + result);

  }

  @Override
  public void sendEvent(int type, String value) {
    sendEventToRemote(type, value + " remote");
  }

  public void sendEventToRemote(int type, String value) {
    int count = ();
    (TAG, "remote callback count:" + count);
    if (count > 0) {
      final int size = ();
      for (int i = 0; i < size; i++) {
        IEventCallback cb = (i);
        try {
          if (cb != null) {
            (type, value);
          }
        } catch (RemoteException e) {
          ();
          (TAG, "remote exception:" + ());
        }
      }
      ();
    }
  }
}

The server inherits from , and implements the corresponding three methods. It should be noted that due to the callback function, the registered IEventCallback is added to the linked list. RemoteCallbackList is used here. The reason why ordinary List or Map cannot be used is that when cross-process calls, when App calls registerCallback and unregisterCallback, even if the same IEventCallback object is passed every time, when it goes to the server, after cross-process processing, different objects will be generated. Therefore, it cannot be judged whether it is the same client object by directly comparing whether it is the same object. The classes specially used in Android to handle cross-process calls are RemoteCallbackList and RemoteCallbackList It can also automatically handle abnormal deaths on the App side, which will automatically remove the registered callbacks.

RemoteCallbackList is very simple to use. Register and remove calls register() and unregister() respectively. It is a little bit troublesome to traverse all Callbacks. For the code, refer to the sendEventToRemote() method above.

As you can see, the system service logic we used for testing is very simple. Register and remove Callback to call RemoteCallbackList corresponding method. The sendEvent() method is called on the App side, and adds "remote" to the string and then calls back to the App. Each method also adds log to facilitate understanding of the process, and the server code is completed.

V. Register the system service

After the code is written, we need to register in SystemServer. All system services are run in a process called system_server. We need to add the written service. There are many services in SystemServer. We add our system services to the end. The corresponding path and code are as follows:

frameworks/base/services/java/com/android/server/

import ;
import ;

/**
 * Starts a miscellaneous grab bag of stuff that has yet to be refactored
 * and organized.
 */
private void startOtherServices() {
  // Some codes are omitted...  // start SystemEventService
  try {
    (,
          new SystemEventService(mSystemContext));
  } catch (Throwable e) {
    reportWtf("starting SystemEventService", e);
  }
  // Some codes are omitted...}

Add the service to SystemServer through ServiceManager, use the name, and the service will be obtained by the name after obtaining it. At this time, if you compile and run it directly, the following error will appear after starting the computer:

E SystemServer:

E SELinux : avc:  denied  { add } for service=test_systemevent pid=1940 uid=1000 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0

This does not have Selinux permissions, we need to add the permission to add the service, the code is as follows:

First define the type, test_systemevent should be consistent with the name used to add the service.

system/sepolicy/service_contexts

wifiscanner                u:object_r:wifiscanner_service:s0
wifi                   u:object_r:wifi_service:s0
window                  u:object_r:window_service:s0
# Some codes are omitted...test_systemevent             u:object_r:test_systemevent_service:s0
*                     u:object_r:default_android_service:s0

system/sepolicy/

# Add the test_systemevent_service type just defined to indicate that it is a system servicetype test_systemevent_service, system_api_service, system_server_service, service_manager_type;

After adding the above code, the service will run normally after the compilation is flashed and turned on.

6. Register Manager

The system service is running, and the next question is how to obtain the App. When the App obtains the system service, we also use a general interface:

()

Before calling getSystemService(), you need to register first, the code is as follows:

frameworks/base/core/java/android/app/

import ;
import ;

static { 
  // Some codes are omitted, refer to other codes, register Manger  registerService(, ,
      new CachedServiceFetcher&lt;SystemEventManager&gt;() {
    @Override
    public SystemEventManager createService(ContextImpl ctx) {
      // Get services      IBinder b = ();
      // Convert to ISystemEvent      ISystemEvent service = (b);
      return new SystemEventManager((), service);
    }});
}

After registering, if you get the Manager and call the interface through getSystemService(); in the App, you will find another error, and Selinux permissions problem:

E SELinux : avc:  denied  { find } for service=test_systemevent pid=4123 uid=10035 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:test_systemevent_service:s0 tclass=service_manager permissive=0

It is said that there is no find permission, so you need to add permissions. The code is modified as follows:

system/sepolicy/untrusted_app.te

# Allow untrusted_app to find test_systemevent_serviceallow untrusted_app test_systemevent_service:service_manager find;

If you are interested in learning this Selinux knowledge, you will add permissions to the corresponding file according to the error message.

At this point, the system code has been modified, the compilation system is flashed, and the following is called through the App.

7. App calls

File copy and preparation:
We need to copy three files into the App, two AIDL files, and one Manager file:



All AIDL files and java files must be consistent with the system's package name and path in the App project. These three files cannot be modified by the App, unless the corresponding modification is also made in the system's source code. In general, these three files must be completely consistent with the App and the system, and the class name package name and package path must be consistent. After copying these three files into the project, after compiling, the call method is as follows.

Get services:

SystemEventManager eventManager = (SystemEventManager)
    ();

Here Android Studio may report an error that the getSystemService() parameter is not a service in Context, and it can be ignored directly and does not affect compilation.

Register/Unregister:

(eventCallback);

(eventCallback);

private  eventCallback = new () {
  @Override
  public void onSystemEvent(int type, String value) throws RemoteException {
    ("SystemEvent", "type:" + type + " value:" + value);
  }
};

Called:

(1, "test string");

The test log is as follows:

D SystemEventManager: SystemEventManager init
D SystemEventService: register pid:3944 uid:10035 result:true
D SystemEventService: remote callback count:1
D SystemEvent: type:1 value:test string remote
D SystemEventService: unregister pid:3944 uid:10035 result:true

You can see that the server was called and the string spliced ​​by the server was successfully received.

8. Add some JNI code

We generally add system services, which may be to call the code in the driver. All JNI parts are generally used. This is not about how to write JNI code, but about the existing JNI code in the system services. We can directly add our functions on this basis.

The JNI part code location is:

frameworks/base/services/core/jni/

The corresponding mk of the compilation is:

frameworks/base/services/
frameworks/base/services/core/jni/

This part of the code is directly compiled into the libandroid_servers dynamic library and loaded in SystemServer:
frameworks/base/services/java/com/android/server/

// Initialize native services.
("android_servers");

If you need to add some JNI code, add the corresponding files directly in the frameworks/base/services/core/jni/ directory.
Add new files to frameworks/base/services/core/jni/ for compilation.
At the same time, according to the JNI function registration method in the existing file, write the corresponding registration method and unify it.
Dynamically register functions in frameworks/base/services/core/jni/.
Regarding JNI dynamic registration knowledge, you can refer to an article I wrote before: Two JNI registration methods

9. Summary

From the above complete process, we basically understand how we usually call getSystemService(). Overall, it is not troublesome. Cross-process calls with real technical content are hidden. We just need to call the interface according to the rules. The above is the complete process of adding a system service and App calls to the Android system. If you have any questions, please feel free to discuss it!

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.