1. 程式人生 > >Android系統新增一個自己的service

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除錯,所以只能刷整包驗證。