从Jni到Java增加一个系统服务SystemServer,并通过getSystemService调用



这里假设我们需要增加的服务名字为AudioTest。

Java层
我们先定义这个服务的接口,文件名为IAudioTest.aidl。里面包含这个服务所有的功能函数
IAudioTest.aidl: frameworks\base\core\java\android\service\audiotest\ IAudioTest .aidl
    
    
package android.service.audiotest;
 
interface IAudioTest {
int init();
// int chooseMainSrc(long src);
int mainChooseChannel(long srcId);
int mainInputGain(long gain);
//...
}
注意这个类的包名和类所放的目录名,需要一一对应。其中的audiotest文件夹需要自己创建的,后面没有出现的文件夹也是需要自己创建

定义好接口文件后,就把接口文件放入Android.mk里被编译
路径是:frameworks\base\Android.mk
    
    
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
core/java/android/accounts/IAccountManager.aidl \
core/java/android/service/audiotest/IAudioTest.aidl \
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
把IAudioTest.adil放入LOCAL_SRC_FILES变量里,可以放入其中的任意一行。如果放在中间注意前后都要有“\",表示和下一行连接。如果IAudioTest.adil放在最后一行后面就不用接"\"

接口已经定义好,接下来就需要实现这些接口
实现的路径及文件名为:frameworks\base\services\java\com\android\server\audiotest\AudioTestService.java
AudioTestService需要继承IAudioTest.Stub,这里Android的Binder机制。
    
    
package com.android.server.audiotest;
 
import android.service.audiotest.IAudioTest;
import android.content.Context;
 
 
public class AudioTestService extends IAudioTest.Stub{
 
private String TAG = "AudioTestService ";
private Context context;
 
private native int AudioTestInitNative();
private native int AudioTestUninitNative();
private native int AudioTestCtrlNative(int cmd, long arg);
 
public AudioTestService(Context c){
context = c;
init();
}
private int myAudioTestCtrl(AudioCtrl ac, long arg){
return AudioTestCtrlNative(ac.ordinal(), arg);
}
 
@Override
public int init(){
int ret = 0;
AudioTestInitNative();
return ret;
}
 
@Override
public int mainChooseChannel(long srcId){
int ret;
ret=myAudioTestCtrl(AudioCtrl.IOCTL_MAIN_CHANNEL, srcId);
return ret;
}
 
@Override
public int mainInputGain(long gain){
int ret;
ret= myAudioTestCtrl(AudioCtrl.IOCTL_MAIN_INPUT_GAIN, gain);
return ret;
}
}

这样就实现了IAudioTest.aidl接口。上面调用了一些Jni接口,我们等会再说如何添加Jni服务到系统中
接下来就是把这个服务加入到ServiceManager中
frameworks\base\services\java\com\android\server\SystemServer.java

SystemServer.java是的initAndLoop()函数会在一开机的时候被调用,做基本的系统服务初始化。里面有很多 ServiceManager.addService的调用,用来添加系统服务。我们也依照原有的服务来做。我选择了在ServiceManager.addService(Context.ALARM_SERVICE, alarm);这一句话后面添加我们有服务:
    
    
//....
AlarmManagerService alarm = null;
AudioTestService audiotest = null;
MountService mountService = null;
 
//....
Slog.i(TAG, "Alarm Manager");
alarm = new AlarmManagerService(context);
ServiceManager.addService(Context.ALARM_SERVICE, alarm);
 
audiotest = new AudioTestService(context);
ServiceManager.addService(Context.AUDIOTEST_SERVICE, audiotest);
 
Slog.i(TAG, "Init Watchdog");
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
Watchdog.getInstance().addThread(wmHandler, "WindowManager thread");
ServiceManager.addService的第一个参数是服务名,在frameworks\base\core\java\android\content\Context.java中添加:
    
    
public static final String POWER_SERVICE = "power";
 
public static final String AUDIOTEST_SERVICE = "audiotest";
 
public static final String WINDOW_SERVICE = "window";
至此,已经定义了服务接口,实现了服务接口,也把服务添加进了我们的驱动中,但我们平时在APK里调用服务都使用了context.getSystemService()的接口。那我们如何通过这个接口来调用我们的服务呢?
在Context的实现类ContextImpl里的一个static{}域里注册了服务,getSystemService()返回的就是这些被注册的服务。我们平时调用 getSystemService()获得的服务一般都是被经过二次封装,只开放了部分的接口到用户层,并不会把我们的IAudioTest.aidl里面的接口全部开放,当然,你也可以全部开放你的接口。
    
    
import android.service.audiotest.IAudioTest;
 
//...
registerService(POWER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
//获得了aidl的IBinder后交给了另一个类进行封闭,保护一些敏感接口
return new PowerManager(ctx.getOuterContext(),
service, ctx.mMainThread.getHandler());
}});
 
registerService(AUDIOTEST_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(AUDIOTEST_SERVICE);
//直接把从ServiceManager获取了原始服务,并把这个服务无保留返回至用户层
IAudioTest service = IAudioTest.Stub.asInterface(b);
return service;
}});
//...
已经全部添加完成。在Android里你添加了系统adil,修改了系统的API,就需要重新更新一个api文件。frameworks\base\api\current.txt。里面记录着很多系统变量和aidl接口。我们可以用命令make update-api来更新这个文件,如果无法更新,可以把这个文件直接删除,再运行make update-api。更新了之后我们定义的adil接口和我们在Context定义的服务名AUDIOTEST_SERVICE,都会在这里出现。

这时候你可以在APP里 getSystemService(“you service name”)来获取你的服务。我们定义的service name为“audiotest”。所有我们可以通过 getSystemService(“ audiotest ”)来获取我们的在registerService注册的服务,我们是无二次封闭,直接返回了AudioTestService。但问题又来了,eclipse里怎么会识别你AudioTestService类呢,肯定会有红线报错啊。这里我们有两个方法把错误去掉,让程序顺利编译。

方法一.  导入Framework的Jar包:
系统不同,芯片平台不同都有可以导致Jar生成的位置不一样
MTK Android4.4的在:out\target\product\my_prj\obj\JAVA_LIBRARIES\framework-base_intermediates\javalib.jar
有些平台在: out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\classes.jar
可以在out目录下搜索一下,MTK就会有几个Frameworks,Framework2,Framework_base。里面都有Frameworks Jar包,具体哪一个可以用RAR打开看一下里面有很多包名,类名。

找到Jar包后导入eclipse
选择项目属性->Java Build Path->Libraries->Add Library->User Library-> User Libraries进入到User Libraries管理界面,点击New新建一个User Library,比如android_framework,点击Add Jars把Jar包加入到建立的User Library中,最后点击OK就可以了。但问题来了,发现在APP里只可以使用IAudioTest,不可以使用AudioTestService。原来我们只可以使用在current.txt里出现的API,其余的无法使用。我们的getSystemService注册的就是IAudioTest,所以我们直接用IAudioTest也可以的。如果你是通过二次包装了,你的二次包装类放在frameworks\base\core\java\android\os也是可以被读到的。为什么这样呢,我们所有的API来源文件夹在build/core/pathmap.mk的FRAMEWORKS_BASE_SUBDIRS变量里定义了,其中就有 frameworks\base\core 目录。注意更新API后要update-api哦

文件二.  使用aidl:
这个文件比较简单,只要在你的APP里新建一个与aidl相同的包名android.service.audiotest,然后再把IAudioTest.adil放入到这个包里,就可以在源码里使用IAudioTest了。

在Java层添加一个服务并到APP里运用就这样完成了

Jni层
在frameworks\base\services\jni\目录下添加
com_android_server_audiotest_AudioTestService.cpp
    
    
#define AUDIO_TEST_NAME "/dev/audiotest"
 
namespace android
{
 
//driver file description
static int fd = -1;
 
static jint AudioTestInit(JNIEnv *env, jobject thiz)
{
fd = open(AUDIO_TEST_NAME, O_RDWR);
if(fd < 0)
{
ALOGE("Open AUDIO_TEST_NAME failed!");
return -1;
}
return fd;
}
 
static jint AudioTestCtrl(JNIEnv *env, jobject thiz, int cmd, long arg)
{
ALOGI("AudioTestCtrl %d", cmd);
return ioctl(fd, cmd, (unsigned long)arg);
}
 
static jint AudioTestUninit(JNIEnv *env, jobject thiz)
{
close(fd);
return 0;
}
 
static JNINativeMethod method_table[] = {
//此Native函数与Java调用函数名对应
{ "AudioTestInitNative", "()I", (void*)AudioTestInit },
{ "AudioTestUninitNative", "()I", (void*)AudioTestUninit },
{ "AudioTestCtrlNative", "(IJ)I", (void*)AudioTestCtrl },
};
 
int register_android_server_audiotest_AudioTestService(JNIEnv *env)
{
//method_table与Java包名类名绑定
return jniRegisterNativeMethods(env, "com/android/server/audiotest/CarAudioService",
method_table, NELEM(method_table));
}
 
};
把上面的注册函数register_android_server_audiotest_AudioTestService(JNIEnv *env);加入到onload.cpp里被调用
     
     
namespace android {
/...
int register_android_service_eposervice(JNIEnv* env);
int register_android_server_audiotest_AudioTestService(JNIEnv* env);
};
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
//...
register_android_server_ConsumerIrService(env);
register_android_service_eposervice(env);
register_android_server_audiotest_AudioTestService(env);
/...
return JNI_VERSION_1_4;
}

实现好后把文件加入到Android.mk里
frameworks\base\services\jni\Android.mk
    
    
LOCAL_SRC_FILES:= \
com_android_server_power_PowerManagerService.cpp \
com_android_server_caraudio_CarAudioService.cpp \
com_android_server_SerialService.cpp \
//...
这里假设我们需要增加的服务名字为AudioTest。

Java层
我们先定义这个服务的接口,文件名为IAudioTest.aidl。里面包含这个服务所有的功能函数
IAudioTest.aidl: frameworks\base\core\java\android\service\audiotest\ IAudioTest .aidl
     
     
package android.service.audiotest;
 
interface IAudioTest {
int init();
// int chooseMainSrc(long src);
int mainChooseChannel(long srcId);
int mainInputGain(long gain);
//...
}
注意这个类的包名和类所放的目录名,需要一一对应。其中的audiotest文件夹需要自己创建的,后面没有出现的文件夹也是需要自己创建

定义好接口文件后,就把接口文件放入Android.mk里被编译
路径是:frameworks\base\Android.mk
     
     
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
core/java/android/accounts/IAccountManager.aidl \
core/java/android/service/audiotest/IAudioTest.aidl \
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
把IAudioTest.adil放入LOCAL_SRC_FILES变量里,可以放入其中的任意一行。如果放在中间注意前后都要有“\",表示和下一行连接。如果IAudioTest.adil放在最后一行后面就不用接"\"

接口已经定义好,接下来就需要实现这些接口
实现的路径及文件名为:frameworks\base\services\java\com\android\server\audiotest\AudioTestService.java
AudioTestService需要继承IAudioTest.Stub,这里Android的Binder机制。
     
     
package com.android.server.audiotest;
 
import android.service.audiotest.IAudioTest;
import android.content.Context;
 
 
public class AudioTestService extends IAudioTest.Stub{
 
private String TAG = "AudioTestService ";
private Context context;
 
private native int AudioTestInitNative();
private native int AudioTestUninitNative();
private native int AudioTestCtrlNative(int cmd, long arg);
 
public AudioTestService(Context c){
context = c;
init();
}
private int myAudioTestCtrl(AudioCtrl ac, long arg){
return AudioTestCtrlNative(ac.ordinal(), arg);
}
 
@Override
public int init(){
int ret = 0;
AudioTestInitNative();
return ret;
}
 
@Override
public int mainChooseChannel(long srcId){
int ret;
ret=myAudioTestCtrl(AudioCtrl.IOCTL_MAIN_CHANNEL, srcId);
return ret;
}
 
@Override
public int mainInputGain(long gain){
int ret;
ret= myAudioTestCtrl(AudioCtrl.IOCTL_MAIN_INPUT_GAIN, gain);
return ret;
}
}

这样就实现了IAudioTest.aidl接口。上面调用了一些Jni接口,我们等会再说如何添加Jni服务到系统中
接下来就是把这个服务加入到ServiceManager中
frameworks\base\services\java\com\android\server\SystemServer.java

SystemServer.java是的initAndLoop()函数会在一开机的时候被调用,做基本的系统服务初始化。里面有很多 ServiceManager.addService的调用,用来添加系统服务。我们也依照原有的服务来做。我选择了在ServiceManager.addService(Context.ALARM_SERVICE, alarm);这一句话后面添加我们有服务:
     
     
//....
AlarmManagerService alarm = null;
AudioTestService audiotest = null;
MountService mountService = null;
 
//....
Slog.i(TAG, "Alarm Manager");
alarm = new AlarmManagerService(context);
ServiceManager.addService(Context.ALARM_SERVICE, alarm);
 
audiotest = new AudioTestService(context);
ServiceManager.addService(Context.AUDIOTEST_SERVICE, audiotest);
 
Slog.i(TAG, "Init Watchdog");
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
Watchdog.getInstance().addThread(wmHandler, "WindowManager thread");
ServiceManager.addService的第一个参数是服务名,在frameworks\base\core\java\android\content\Context.java中添加:
     
     
public static final String POWER_SERVICE = "power";
 
public static final String AUDIOTEST_SERVICE = "audiotest";
 
public static final String WINDOW_SERVICE = "window";
至此,已经定义了服务接口,实现了服务接口,也把服务添加进了我们的驱动中,但我们平时在APK里调用服务都使用了context.getSystemService()的接口。那我们如何通过这个接口来调用我们的服务呢?
在Context的实现类ContextImpl里的一个static{}域里注册了服务,getSystemService()返回的就是这些被注册的服务。我们平时调用 getSystemService()获得的服务一般都是被经过二次封装,只开放了部分的接口到用户层,并不会把我们的IAudioTest.aidl里面的接口全部开放,当然,你也可以全部开放你的接口。
     
     
import android.service.audiotest.IAudioTest;
 
//...
registerService(POWER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
//获得了aidl的IBinder后交给了另一个类进行封闭,保护一些敏感接口
return new PowerManager(ctx.getOuterContext(),
service, ctx.mMainThread.getHandler());
}});
 
registerService(AUDIOTEST_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(AUDIOTEST_SERVICE);
//直接把从ServiceManager获取了原始服务,并把这个服务无保留返回至用户层
IAudioTest service = IAudioTest.Stub.asInterface(b);
return service;
}});
//...
已经全部添加完成。在Android里你添加了系统adil,修改了系统的API,就需要重新更新一个api文件。frameworks\base\api\current.txt。里面记录着很多系统变量和aidl接口。我们可以用命令make update-api来更新这个文件,如果无法更新,可以把这个文件直接删除,再运行make update-api。更新了之后我们定义的adil接口和我们在Context定义的服务名AUDIOTEST_SERVICE,都会在这里出现。

这时候你可以在APP里 getSystemService(“you service name”)来获取你的服务。我们定义的service name为“audiotest”。所有我们可以通过 getSystemService(“ audiotest ”)来获取我们的在registerService注册的服务,我们是无二次封闭,直接返回了AudioTestService。但问题又来了,eclipse里怎么会识别你AudioTestService类呢,肯定会有红线报错啊。这里我们有两个方法把错误去掉,让程序顺利编译。

方法一.  导入Framework的Jar包:
系统不同,芯片平台不同都有可以导致Jar生成的位置不一样
MTK Android4.4的在:out\target\product\my_prj\obj\JAVA_LIBRARIES\framework-base_intermediates\javalib.jar
有些平台在: out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\classes.jar
可以在out目录下搜索一下,MTK就会有几个Frameworks,Framework2,Framework_base。里面都有Frameworks Jar包,具体哪一个可以用RAR打开看一下里面有很多包名,类名。

找到Jar包后导入eclipse
选择项目属性->Java Build Path->Libraries->Add Library->User Library-> User Libraries进入到User Libraries管理界面,点击New新建一个User Library,比如android_framework,点击Add Jars把Jar包加入到建立的User Library中,最后点击OK就可以了。但问题来了,发现在APP里只可以使用IAudioTest,不可以使用AudioTestService。原来我们只可以使用在current.txt里出现的API,其余的无法使用。我们的getSystemService注册的就是IAudioTest,所以我们直接用IAudioTest也可以的。如果你是通过二次包装了,你的二次包装类放在frameworks\base\core\java\android\os也是可以被读到的。为什么这样呢,我们所有的API来源文件夹在build/core/pathmap.mk的FRAMEWORKS_BASE_SUBDIRS变量里定义了,其中就有 frameworks\base\core 目录。注意更新API后要update-api哦

文件二.  使用aidl:
这个文件比较简单,只要在你的APP里新建一个与aidl相同的包名android.service.audiotest,然后再把IAudioTest.adil放入到这个包里,就可以在源码里使用IAudioTest了。

在Java层添加一个服务并到APP里运用就这样完成了

Jni层
在frameworks\base\services\jni\目录下添加
com_android_server_audiotest_AudioTestService.cpp
     
     
#define AUDIO_TEST_NAME "/dev/audiotest"
 
namespace android
{
 
//driver file description
static int fd = -1;
 
static jint AudioTestInit(JNIEnv *env, jobject thiz)
{
fd = open(AUDIO_TEST_NAME, O_RDWR);
if(fd < 0)
{
ALOGE("Open AUDIO_TEST_NAME failed!");
return -1;
}
return fd;
}
 
static jint AudioTestCtrl(JNIEnv *env, jobject thiz, int cmd, long arg)
{
ALOGI("AudioTestCtrl %d", cmd);
return ioctl(fd, cmd, (unsigned long)arg);
}
 
static jint AudioTestUninit(JNIEnv *env, jobject thiz)
{
close(fd);
return 0;
}
 
static JNINativeMethod method_table[] = {
//此Native函数与Java调用函数名对应
{ "AudioTestInitNative", "()I", (void*)AudioTestInit },
{ "AudioTestUninitNative", "()I", (void*)AudioTestUninit },
{ "AudioTestCtrlNative", "(IJ)I", (void*)AudioTestCtrl },
};
 
int register_android_server_audiotest_AudioTestService(JNIEnv *env)
{
//method_table与Java包名类名绑定
return jniRegisterNativeMethods(env, "com/android/server/audiotest/CarAudioService",
method_table, NELEM(method_table));
}
 
};
把上面的注册函数register_android_server_audiotest_AudioTestService(JNIEnv *env);加入到onload.cpp里被调用
      
      
namespace android {
/...
int register_android_service_eposervice(JNIEnv* env);
int register_android_server_audiotest_AudioTestService(JNIEnv* env);
};
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
//...
register_android_server_ConsumerIrService(env);
register_android_service_eposervice(env);
register_android_server_audiotest_AudioTestService(env);
/...
return JNI_VERSION_1_4;
}

实现好后把文件加入到Android.mk里
frameworks\base\services\jni\Android.mk
     
     
LOCAL_SRC_FILES:= \
com_android_server_power_PowerManagerService.cpp \
com_android_server_caraudio_CarAudioService.cpp \
com_android_server_SerialService.cpp \
//...

猜你喜欢

转载自blog.csdn.net/shell812/article/details/49819599