当我们的应用程序需要使用或修改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模块所使用的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就可以。