Adding a Custom Service in Android System

This document describes how to add a custom service to the Android system. This example was implemented on Android 7.0, so code locations may vary on different systems.

1. Design the Interface

Create a new folder named addservice under the frameworks/base directory. Inside addservice, create the following structure:

  • Android.mk
  • java/android/mymodule/test (This path can be customized)

The java/android/mymodule/test directory will contain the Java interface files and corresponding AIDL files.

The Android.mk file under frameworks/base/addservice/ should look like this:

1
2
3
4
5
6
7
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, java) LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_MODULE := mymodule include $(BUILD_JAVA_LIBRARY)

2. Write the Interface

Create an AIDL file named ITestManager.aidl under frameworks/base/addservice/java/android/mymodule/test. This example includes a simple test method:

1
2
3
4
5
6
7
8
9
package android.mymodule.test;
/** * {@hide} */
interface ITestManager {
void testMethod();
}

Create the corresponding TestManager.java file. TestManager is an operation class; the actual implementation is in TestService.java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package android.mymodule.test;
import android.util.Slog;
import android.os.RemoteException;
public class TestManager {
private final ITestManager mService;
public TestManager(ITestManager mService) {
// Here, ITestManager is passed in. Check other system services for similar implementations.
this.mService = mService;
}
public void testMethod() {
try {
mService.testMethod();
Slog.i("add_service_test", "TestManager testMethod");
} catch (RemoteException ex) {
ex.printStackTrace(); } } }

Create TestService.java in the frameworks/base/services/core/java/com/android/server/ directory. This directory contains other services like BatteryService.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.android.server;
import android.content.Context;
import android.util.Slog;
import android.mymodule.test.ITestManager;
// ITestManager.Stub is a fixed pattern.
public class TestService extends ITestManager.Stub {
private final Context mContext;
public TestService(Context context) {
super();
mContext = context; }
public void testMethod() {
// Test method. Add a log to check execution.
Slog.i("add_service_test", "TestService testMethod");
} }

3. Modify Build Files

Since a new directory addservice was added under frameworks/base, update /build/core/pathmap.mk by adding it to FRAMEWORKS_BASE_SUBDIRS. Add the following line:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FRAMEWORKS_BASE_SUBDIRS := \ $(addsuffix /java, \ core \ graphics \ location \ media \ media/mca/effect \ media/mca/filterfw \ media/mca/filterpacks \ drm \ opengl \ sax \ telecomm \ telephony \ wifi \ keystore \ rs \ addservice \

Modify /frameworks/base/Android.mk. Add these lines:

  • addservice/java/android/mymodule/test/ITestManager.aidl \
  • android/mymodule/test
1
2
3
4
5
LOCAL_SRC_FILES += \ core/java/android/service/quicksettings/IQSTileService.aidl \ telephony/java/com/mediatek/internal/telephony/ITelephonyEx.aidl \ telephony/java/com/mediatek/internal/telephony/ISetDefaultSubResultCallback.aidl \ addservice/java/android/mymodule/test/ITestManager.aidl \
1
2
3
4
5
6
7
packages_to_document := \ android \ javax/microedition/khronos \ org/apache/http/conn \ org/apache/http/params org/apache/http/params \ android/mymodule/test

Modify alps/build/target/product/base.mk and alps/build/target/product/generic_no_telephony.mk to include the module name defined in Android.mk.

1
2
3
4
5
6
7
8
9
--- a/alps/build/target/product/generic_no_telephony.mk +++ b/alps/build/target/product/generic_no_telephony.mk
@@ -28,7 +28,8 @@ PRODUCT_PACKAGES := \
Provision \ SystemUI \ EasterEgg \ - WallpaperCropper + WallpaperCropper \ + mymodule
1
2
3
4
5
6
7
8
9
--- a/alps/build/target/product/generic_no_telephony.mk +++ b/alps/build/target/product/generic_no_telephony.mk
@@ -28,7 +28,8 @@ PRODUCT_PACKAGES := \
Provision \ SystemUI \ EasterEgg \ - WallpaperCropper + WallpaperCropper \ + mymodule

4. Add the Service to System Server

In framework/base/services/java/com/android/server/SystemServer.java, add the service like this:

1
2
3
4
5
6
7
8
9
10
11
--- a/alps/frameworks/base/services/java/com/android/server/SystemServer.java +++ b/alps/frameworks/base/services/java/com/android/server/SystemServer.java
@@ -683,6 +683,12 @@ public final class SystemServer {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mSystemServiceManager.startService(TelecomLoaderService.class);
TestService test = new TestService(context);
ServiceManager.addService(Context.TEST_SERVICE, test);
Slog.i("add_service_test", "SystemServer add service");
traceBeginAndSlog("StartTelephonyRegistry");

Add Context.TEST_SERVICE in Context.java:

1
public static final String TEST_SERVICE= "test";

Register the service in SystemServiceRegistry.java (or ContextImpl in older versions):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--- a/alps/frameworks/base/core/java/android/app/SystemServiceRegistry.java +++ b/alps/frameworks/base/core/java/android/app/SystemServiceRegistry.java
@@ -158,6 +158,11 @@ import com.mediatek.usp.UspManager;
/** * Manages all of the system services that can be returned by {@link Context#getSystemService}. * Used by {@link ContextImpl}. */
@@ -177,6 +182,17 @@ final class SystemServiceRegistry {
private SystemServiceRegistry() { }
static {
registerService(Context.TEST_SERVICE,TestManager.class,
new CachedServiceFetcher(){
@Override
public TestManager createService(ContextImpl ctx)
{ IBinder b = ServiceManager.getService(Context.TEST_SERVICE);
Log.i("add_service_test","SystemServiceRegistry registerService method");
return new TestManager(ITestManager.Stub.asInterface(b));
}});

5. Security Configuration

Without these modifications, the system may fail to boot due to security issues. The naming must match the Context definition, converting uppercase to lowercase. The file location may vary.

Add the service to /alps/system/sepolicy/service.te:

1
2
3
4
5
6
7
--- a/alps/system/sepolicy/service.te +++ b/alps/system/sepolicy/service.te
@@ -119,3 +119,4 @@ type wifip2p_service, app_api_service, system_server_service, service_manager_ty
type wifiscanner_service, system_api_service, system_server_service, service_manager_type; type wifi_service, app_api_service, system_server_service, service_manager_type; type window_service, system_api_service, system_server_service, service_manager_type; +type test_service, system_api_service, system_server_service, service_manager_type;

Grant service permissions in /external/sepolicy/service_contexts:

1
2
3
4
5
6
7
--- a/alps/system/sepolicy/service_contexts +++ b/alps/system/sepolicy/service_contexts
@@ -144,4 +144,5 @@ wifip2p u:object_r:wifip2p_service:s0
wifiscanner u:object_r:wifiscanner_service:s0 wifi u:object_r:wifi_service:s0 window u:object_r:window_service:s0 +test u:object_r:test_service:s0

With the code modifications complete, proceed to compilation.

6. Compilation

Run make update-api in the root directory. Compile framework.jar, then service.jar, and finally mymodule. Use mm for individual module compilation, followed by a full build for verification. Due to the presence of boot.art and boot.oat in the out directory, framework.jar and service.jar cannot be pushed for debugging, requiring a full system flash for validation.