前段时间找工作,被面试官问到这样一个问题,怎样在系统里面添加一个service。我只知道个大概,自己还没有去加过。这次有空,就试着自己添加,并记录下来。我是在android 7.0系统添加的,不同系统代码位置可能会有差异。
1.设计接口
在/frameworks/base目录下新建一个文件夹addservice, 在addservice目录下新建Android.mk和/java/android/mymodule/test, 可以根据自己的需要命名。
/frameworks/base/addservice/java/android/mymodule/test目录下存放封装接口的java文件和对应的aidl文件。
/frameworks/base/addservice/目录下的Android.mk如下:
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)
下面就是写这个接口了
在/frameworks/base/addservice/java/android/mymodule/test下新建一个aidl文件,命名为ITestManager.aidl,我这里就写一个测试的方法,没什么实际意义,内容为:
package android.mymodule.test;
/**
* {@hide}
*/
interface ITestManager {
void testMethod();
}
对应的TestManager.java,TestManager只是一个操作类,真正的实现是在TestService.java:
package android.mymodule.test;
import android.util.Slog;
import android.os.RemoteException;
public class TestManager {
private final ITestManager mService;
public TestManager(ITestManager mService) {
//这里把ITestManager传进来,可以看看系统其它service,都是这样写的
this.mService = mService;
}
public void testMethod() {
try {
mService.testMethod();
Slog.i("add_service_test", "TestManager testMethod");
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
在frameworks/base/services/core/java/com/android/server/文件夹创建一个TestService.java,这个文件夹有很多的其它service,像BatteryService
package com.android.server;
import android.content.Context;
import android.util.Slog;
import android.mymodule.test.ITestManager;
//这里的ITestManager.Stub是固定写法
public class TestService extends ITestManager.Stub {
private final Context mContext;
public TestService(Context context) {
super();
mContext = context;
}
public void testMethod() {
// 测试方法,为了测试执行情况,在这里加log
Slog.i("add_service_test", "TestService testMethod");
}
}
由于我们在frameworks/base目录下增加了一个新的目录/addservice, 所以需要在/build/core/pathmap.mk中增加到FRAMEWORKS_BASE_SUBDIRS,注意最后一句,原先是没有的,需要我们自己加:
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 \
)
/frameworks/base/Android.mk也要修改,
注意增加的这两句:
addservice/java/android/mymodule/test/ITestManager.aidl \
android/mymodule/test
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 \
packages_to_document := \
android \
javax/microedition/khronos \
org/apache/http/conn \
org/apache/http/params
org/apache/http/params \
android/mymodule/test
我加了这里后,编译是没有问题的,但是整体编译时却没有把jar包编出来。后来发现还需要修改alps/build/target/product/base.mk 和 alps/build/target/product/generic_no_telephony.mk,把要编译的模块名写进去,跟自己定义的Android.mk中保持一致
--- 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
--- 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.将新增的service添加到system server.
在/framework/base/services/java/com/android/server/SystemServer.java,addService,像这样:
--- 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");
这里的Context.TEST_SERVICE ,当然要自己在Context中添加。
public static final String TEST_SERVICE= "test";
另外,还要在SystemServiceRegistry中注册这一service,注意7.0的代码是这个类,7.0以下的代码可能是ContextImpl这个类:
--- 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.特别注意,如果没有下面这两个修改,编译完了之后,不能正常开机,从log中看到是什么安全问题。
这里的命名跟Context中自己添加的保持一致,把大写改成小写。这个文件在不同的代码中位置可能不一样,有些在device目录下。
将服务加入到源码中,编译备份/alps/system/sepolicy/service.te
--- 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;
给服务权限
/external/sepolicy/service_contexts
--- 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
至此,添加系统service的代码就写完了,剩下的就是编译了。
6.编译
回到根目录下执行make update-api,否则编译不能通过。先编译framework.jar,然后编译service.jar,最后编译自己加mymodule。可以用mm命令编译,没问题后再整编。因为编译出来的out目录有boot.art和boot.oat,framework.jar和service.jar不能push调试,所以只能刷整包验证。