Android系統新增一個自己的service
前段時間找工作,被面試官問到這樣一個問題,怎樣在系統裡面新增一個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<TestManager>(){
+ @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除錯,所以只能刷整包驗證。