Android添加系统service

当我们的应用程序需要使用或修改android系统提供的功能时,通常的做法是自己封装接口,接口的实现放在某一个service里,这个service要么是已经存在的,要么是自定义的。然后将这个service添加到SystemServer的ServiceManager, 再在ContextImpl注册该service,并返回我们封装类的对象。 当系统启动的时候,这个service就会被初始化,应用程序通过一个代表service名称的String类型的字符串就可以拿到包含这个service接口的封装类的对象,直接调用该对象提供的API,就相当于调用了底层service的实现。
上面算是概述,接下来该用一个实例来详细描述了。例如我们要实现一个网络防火墙,需要最基本的功能就是开启/关闭防火墙,具体的步骤可以参考以下:

1.设计接口
在/frameworks/base目录下新建一个文件夹che, 在che目录下新建Android.mk和/java/android/myModule/firewall, 斜体部分自己替换。
/frameworks/base/che/java/android/myModule/firewall目录下存放封装接口的java文件和对应的aidl文件。
/frameworks/base/che/目录下的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/che/java/android/myModule/firewall下新建一个aidl文件,命名为IGreenNetworkManager.aidl,内容为:

package android.myModule.firewall;

/**
 * {@hide}
 */

interface IGreenNetworkManager {
    void disableAll();
    void enableAll();
}

对应的GreenNetworkManager.java:

package android.myModule.firewall;

import android.os.RemoteException;

public class GreenNetworkManager {

    private final IGreenNetworkManager mService;

    public GreenNetworkManager(IGreenNetworkManager mService) {
        this.mService = mService;
    }

    public void disableAll() {
        try {
            mService.disableAll();
        } catch (RemoteException ex) {
            ex.printStackTrace();
        }        
    }

    public void enableAll() {
        try {
            mService.enableAll();
        } catch (RemoteException ex) {
            ex.printStackTrace();
        }
    }
}

该文件跟对应的aidl文件处于同一目录下。

2.具体实现
注意上段代码有个IGreenNetworkManager,真正实现功能的应该就是这个接口的实现类了。此接口是编译的时候根据aidl自动生成的。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: frameworks/base/che/java/android/myModule/firewall/IGreenNetworkManager.aidl
 */
package android.myModule.firewall;
/**
 * {@hide}
 */
public interface IGreenNetworkManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.myModule.firewall.IGreenNetworkManager
{
    ...
}

好像是进程间通信的东东,不用太在意。
那么真正实现的部分在哪呢?当然是上面接口的实现类了。通常我们将之命名为xxxService. 这里我们命名为GreenNetworkService.java,位于/frameworks/base/services/java/com/android/server/,长这样子:

package com.android.server;

import android.content.Context;
import android.util.Slog;
import android.myModule.firewall.IGreenNetworkManager;

public class GreenNetworkService extends IGreenNetworkManager.Stub {
    private final Context mContext;
    ...
    public GreenNetworkService(Context context) {
        super();
        mContext = context;
    }

    public void disableAll() {
        // 禁止所有网络访问
        ...
    }

    public void enableAll() {
        // 允许所有网络访问
        ...
    }
}

3.修改编译系统
由于我们在frameworks/base目录下增加了一个新的目录/che, 所以需要在/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 \
        telephony \
        wifi \
        keystore \
        che \
     )

/frameworks/base/Android.mk也要修改,
注意增加的这两句:
- che/java/android/myModule/firewall/IGreenNetworkManager.aidl \
- android/myModule/firewall

LOCAL_SRC_FILES += \
    core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
    ...
    core/java/com/nvidia/NvCPLSvc/INvCPLRemoteService.aidl \
    che/java/android/myModule/firewall/IGreenNetworkManager.aidl \

...
    # the documentation
# ============================================================

# TODO: deal with com/google/android/googleapps
packages_to_document := \
    android \
    javax/microedition/khronos \
    android/myModule/firewall
...

4.将新增的service添加到system server.
在/framework/base/services/java/com/android/server/SystemServer.java,addService,像这样:

public void initAndLoop() {
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
    SystemClock.uptimeMillis());
    Looper.prepareMainLooper();
    ...   
    try {
            Slog.i(TAG, "GreenNetworkService");
            GreenNetworkService greenNetwork = new GreenNetworkService(context);      ServiceManager.addService(Context.GREEN_NETWORK_SERVICE, greenNetwork);
        } catch (Throwable e) {
            Slog.e(TAG, "Failure startingGreenNetworkService", e);
    }
    ...
    Looper.loop();
    Slog.d(TAG, "System ServerThread is exiting!");
}

当然Context.java中还要定义这样一句:

public static final String GREEN_NETWORK_SERVICE = "green_network";

另外,还要在ContextImpl中注册这一service:

private static ServiceFetcher WALLPAPER_FETCHER = new ServiceFetcher() {
    ...
 static {
     ...
     registerService(GREEN_NETWORK_SERVICE, new ServiceFetcher() {
            public Object createService(ContextImpl ctx) {
                IBinder b = ServiceManager.getService(GREEN_NETWORK_SERVICE);
                IGreenNetworkManager service = IGreenNetworkManager.Stub.asInterface(b);
                return new GreenNetworkManager(service);
            }
     });
     ...

至此,添加系统service的代码就写完了,剩下的就是编译了。

5.编译
为了避免编译错误,在更新了/framework/base下面的api之后要先更新一下api, 命令是

make update-api

这一步是更新位于frameworks/base/api/current.txt的api文档。
下面开始正式编译了。通常编译都是按模块来的,那么怎么知道应该编译哪个模块呢?我理解的就是找你修改的文件的同级或上级最接近它的那个Android.mk文件,里面一般会有个变量LOCAL_MODULE,这个变量就代表着这个模块的模块名,我们编译这个模块就好了,编译有两种方式:

  • make LOCAL_MODULE
  • mmm 模块所在的目录

第一种方式make后面跟的是模块名,是编译单个模块,会把该模块及其依赖的其他模块一起编译(会搜索整个源代码来定位LOCAL_MODULE模块所使用的Android.mk文件,还要判断该模块依赖的其他模块是否有修改);第二种方式是编译指定目录下的模块,但不编译它所依赖的其它模块。所以第一种方式会比较慢,具体使用哪种方式依具体情形而定。我选择的原则就是哪种行就用哪种,一般优先选第一种,因为这样我就会很确定我编的是什么模块,毕竟模块名是全局唯一的嘛。
具体到本例,我们主要修改的是/framework/base/che、/framework/base/core和/framework/base/services几个目录下的文件,先找到这几个目录下距离修改文件最近的Android.mk,即/framework/base/Android.mk和/framework/base/services/java/Android.mk, 找到相应的模块名,分别是

LOCAL_MODULE := framework-base
LOCAL_MODULE:= services

剩下的就是编译各个需要的模块了,步骤如下:
- 首先编译framework: make framework-base
- 再编译services: make services
- 最后是供应用程序编译通过的jar: make mymodule
如果修改的文件在其他的模块里,也是按照这样的方法编译。

最后一个问题:应用程序怎样调用我定义的Service API呢?有了以上的操作,这个问题就很容易解决了。将生成的framework.jar和services.jar分别替换掉android设备的 /system/framework/下的framework.jar和services.jar。
具体操作是先挂载设备”adb remount”, 使设备对系统文件具有“写”的权限。然后”adb push xxx/target/product/xxx/system/framework/framework.jar /system/framework/”和”adb push xxx/target/product/xxx/system/framework/services.jar /system/framework/”.替换编译出来的两个jar . 然而,若要应用程序编译通过,还需要一个jar, 就是make mymodule生成的classes.jar,位于xxx/target/common/obj/JAVA_LIBRARIES/mymodule_intermediates/. 将这个classes.jar引入应用程序中,作为依赖库,编译就能通过了。

最后,看看应用程序是如何调用新增的API:

import android.myModule.firewall.GreenNetworkManager;
...
GreenNetworkManager greenNetworkManager = (GreenNetworkManager) mContext.getSystemService(CommonUtil.GREEN_NETWORK_SERVICE);
        if(!restrictEnable) {
            try {
                greenNetworkManager.enableAll();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        } else {
            try {
                greenNetworkManager.disableAll();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            ...
        }

public final class CommonUtil {
    public static final String GREEN_NETWORK_SERVICE = "green_network";
    ....
}

本文通过一个例子详细介绍了如何往android系统添加自定义功能的过程,还没有涉及到修改framework之外的模块,其实过程都类似,比如修改了/system/netd下面的代码,我们只要编译netd这个模块,并将生成的netd替换设备上的/system/bin目录下的netd就可以。

你可能感兴趣的:(Android)