这里假设我们需要增加的服务名字为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);}@Overridepublic int init(){int ret = 0;AudioTestInitNative();return ret;}@Overridepublic int mainChooseChannel(long srcId){int ret;ret=myAudioTestCtrl(AudioCtrl.IOCTL_MAIN_CHANNEL, srcId);return ret;}@Overridepublic 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 descriptionstatic 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);}@Overridepublic int init(){int ret = 0;AudioTestInitNative();return ret;}@Overridepublic int mainChooseChannel(long srcId){int ret;ret=myAudioTestCtrl(AudioCtrl.IOCTL_MAIN_CHANNEL, srcId);return ret;}@Overridepublic 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 descriptionstatic 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 \//...
本文详细介绍了如何在Android系统中添加自定义服务AudioTest,包括定义AIDL接口、实现服务类、注册服务以及JNI层的实现。同时提供了两种解决Eclipse环境中类引用问题的方法。
1万+

被折叠的 条评论
为什么被折叠?



