android设备service从C到java API的构成
2015-07-04 13:23
591 查看
很多时间,我们在android开发的时候要调用硬件资源,
【android源码中】:mVibrator = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));或者直接vibrator = new VibratorService(context);或者Vibrator vibrator = new
SystemVibrator();
【应用API中】: vibrator = (Vibrator)
getSystemService(VIBRATOR_SERVICE) 或者 vibrator = (Vibrator)getApplication().getSystemService(Service.VIBRATOR_SERVICE); 这样就获得了硬件服务,接下来你可以调用Vibrator.vibratorOn()这样的方法来让硬件执行动作。
今天无意翻了翻service及manager相关的源代码,边看边留下痕迹。
NOTE:以下所有路径为相对于源码目录下
android有个总的管理系统服务叫SystemServer,在此启动了系统的核心服务,比如我们这次要说的最简单的设备振子Vibrator就是在这注册进去的。如:
在SystemServer.java中的main函数中先加载由c++写的各种设备JNI库libandroid_servers.so,调用init1(),然后在init1()函数中又回调了init2(),而init2实质上就是添加利用函数ServiceManager.addService()来添加各种核心服务。代码片如下:
必须先加载JNI库(JNI相关不明白详见我的系列文章《HAL/JNI简明笔记》),后面的要添加的各种service才能找到各种硬件设备的native方法,后者依赖前者,是硬件函数C到JAVA的传递接合。
那么这个JNI库有哪些组成?
frameworks\base\services\jni\这个目录下的所有c++文件打包成的,每个c++文件都会注册到具体的java类中,比如com_android_server_VibratorService.cpp提供了vibratorExists,vibratorOn,vibratorOff三个native方法,它会被注册到com.android.server.VibratorService这样的class中,并且只有这个class才能调用者3个native方法。
init1()居然是个native函数,那就是用c++写的嘛。那应该在上面说的JNI库里,果不其然,在com_android_server_SystemServer.cpp中,init1()就是android_server_SystemServer_init1(),然后又去调用system_init()靠!居然又是extern
来的,source insight来帮忙,发现在frameworks\base\cmds\system_server\library\system_init.cpp,这是一个普通共享库libsystem_server.so,在JNI库的Android.mk中的LOCAL_SHARED_LIBRARIES含有libsystem_server.so,这样反正就能用就是了。那我们来看看system_init这个函数代码片如下:
class SystemServer中定义的 public static final void init2()嘛。
好了说白了就一句话,SystemServer调用init1,init1有回调了init2。
驱动路径:kernel\drivers\platform\msm\qpnp-vibrator.c
所在镜像boot.img
内核提供硬件,这里要么提供/dev/...或者/sys/...等,此处不详说,实际上就是让内核vibrator驱动导出一个结点/sys/class/timed_output/vibrator/enable
这个驱动注册的设备是timed_output_dev
2)硬件抽象层(HAL)
HAL路径:hardware\libhardware_legacy\vibrator\vibrator.c
所在镜像system.img中的库/system/lib/libhardware_legacy.so
这个代码就是去读写enable,来组成3个函数vibrator_exists、vibrator_on、vibrator_off。
放在hardware\libhardware_legacy下的HAL编译成的libxax.so是普通意义的库,而在其他目录下编译成的库是利用hardware.c中定义的hw_device_t格式来定制的并编译成libxbx.so库,实际上它不算普通意义上的完整库,它是借尸so的stub,它和hardware.c编译成的libhardware.so共同组成普通意义上的库,因为只有后者才能用前者,JNI并不能直接用stub库。
3)JNI(java native interface)
从这开始代码是framework部分了
JNI路径:frameworks\base\services\jni\com_android_server_VibratorService.cpp
所在镜像system.img中的库/system/lib/libandroid_servers.so
从这个源码文件名就可以看出来c函数导出给java中的哪个包哪个类使用的了,难道不是么?!这3个导出函数为vibratorExists、vibratorOn、vibratorOff,要注意c和java命名的习惯。同样为什么JNI可以调用HAL函数,难道只因为include头文件?是因为JNI的Android.mk指定了LOCAL_SHARED_LIBRARIES := \libhardware_legacy
\
4)service native
源码路径:frameworks\base\services\java\com\android\server\VibratorService.java
既然JNI中把那3个函数注册给该类使用,那么VibratorService.java中需要native声明如下:
native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
native static void vibratorOff();
这样VibratorService类就可以直接使用这3个函数了。
这个类extends IVibratorService.Stub说明是继承了IVibratorService,这个文件是编译时由IVibratorService.aidl自动生成的(见下一步骤5),IVibratorService.java存放于out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\src\core\java\android\os,可以称为service
proxy???
VibratorService.java和IVibratorService.java实现相同的接口IVibratorService.aidl。
5)AIDL(Android Interface Definition Language)
源码路径:frameworks\base\core\java\android\os\IVibratorService.aidl
源码内容也很简单如下:
API,按需导出即可。那么这个systemReady()给谁用呢?答案就在SystemServer.java中添加设备service后调用,表示系统准备好,进入app process。
这个aidl文件有什么用?
对于源码中的类如果你想把其中的某些接口导出去给应用开发者用(常用于新接口或者系统隐藏接口),提供接口者事先根据需要将函数接口放入到IVibratorService.aidl(与提供接口的代码在一个包),然后应用开发者需要在apk源码中引入这个aidl文件,eclipse编译后会在gen目录下生成IVibratorService.java,提供接口者将这个生成的代码放入到IVibratorService.aidl一起编译。这样接口在新烧写的系统就能和应用开发就能打通。
实际上我们需要的是生成后的IVibratorService.java,这时你将IVibratorService.aidl删掉,编译都没问题的。
6)添加service到ServiceManager
源码路径:frameworks\base\services\java\com\android\server\SystemServer.java
从有了IVibratorService.java,有了跨进程调用,导出的函数就不存在权限问题了,下面就是框架的问题了。
上面介绍SystemServer时说道init2,这个函数就是启动一个thread用于添加各种核心服务,其中vibrator服务添加如下:
7)Manager
我们暂且称为manager,因为其它服务命名大多含有单词manager(比如UsbManager,TelephonyManager),此处为硬件服务封装的最后一层类。
源码路径:frameworks\base\core\java\android\os\SystemVibrator.java
SystemVibrator类是继承于抽象类Vibrator,前者的构造函数中调用了
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
这样就获取了VibratorService服务,SystemVibrator.java源码中除了跟IVibratorService.aidl相同的函数,其他函数均为{@hide},也就是说不公开。
8)google API化
源码路径:frameworks\base\core\java\android\app\ContextImpl.java
这其中有个satic 括起来的部分,用于注册硬件服务,此后API可以直接使用,代码片如下
这样之后你在apk中就可以使用getSystemService(Service.VIBRATOR_SERVICE)来获取硬件服务,就可以控制vibrator了。
常用服务如下,具体见context.java
传入的Name | 返回的对象 | 说明
WINDOW_SERVICE WindowManager 管理打开的窗口程序
LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view
ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态
POWER_SERVICE PowerManger 电源的服务
ALARM_SERVICE AlarmManager 闹钟的服务
NOTIFICATION_SERVICE NotificationManager 状态栏的服务
KEYGUARD_SERVICE KeyguardManager 键盘锁的服务
LOCATION_SERVICE LocationManager 位置的服务,如GPS
SEARCH_SERVICE SearchManager 搜索的服务
VEBRATOR_SERVICE Vebrator 手机震动的服务
CONNECTIVITY_SERVICE Connectivity 网络连接的服务
WIFI_SERVICE WifiManager Wi-Fi服务
TELEPHONY_SERVICE TeleponyManager 电话服务
Service Manager 的工作就是登记功能。服务通过add_service方法将自己的名字和Binder 标识handle 登记在svclist 中。而服务请求者,通过check_service方法,通过服务名字在service list 中获取到service 相关联的Binder 的标识handle,通过这个Handle
作为请求包的目标地址发起请求。
通讯:IPC。Android 设计者在Linux内核中设计了一个叫做Binder 的设备文件,专门用来进行Android 的数据交换。从数据流来看Java 对象从Java 的VM 空间进入到C++空间进行了一次转换,并利用C++空间的函数将转换过的对象通过driver\binder 设备传递到服务进程,从而完成进程间的IPC。
(1)从JVM 空间传到c++空间,这个是靠JNI 使用ENV 来完成对象的映射过程。
(2)从c++空间传入内核Binder 设备,使用ProcessState 类完成工作。
(3) Service 从内核中Binder 设备读取数据。
由于我们在写一个Service时,在一个Package中写了Service
Native部分和Service Proxy部分,而Native和Proxy都实现相同的接口:IXXX
Interface,但是一个在服务端,一个在客服端。客户端调用的方式是使用remote->transact方法向Service发出请求,而在服务端的OnTransact中则是处理这些请求。所以在Android
Client空间就看到这个效果:只需要调用代理对象方法就达到了对远程服务的调用目的,实际上这个调用路径好长好长。
PS:怪不得android比IOS慢的??!!
那么普通应用要调用隐藏API咋办?我的解决办法:一、广播;二、写AIDL服务;三、反射(网上有的能成功,有的还是不行)。
frameworks\base\services\jni打包为libandroid_servers.so
2)硬件service(java)
frameworks\base\services\java\打包为services.jar
3)aidl
frameworks\base\core\java\android\os
4)系统命令
frameworks\base\cmds比如上面的system_server
【android源码中】:mVibrator = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));或者直接vibrator = new VibratorService(context);或者Vibrator vibrator = new
SystemVibrator();
【应用API中】: vibrator = (Vibrator)
getSystemService(VIBRATOR_SERVICE) 或者 vibrator = (Vibrator)getApplication().getSystemService(Service.VIBRATOR_SERVICE); 这样就获得了硬件服务,接下来你可以调用Vibrator.vibratorOn()这样的方法来让硬件执行动作。
今天无意翻了翻service及manager相关的源代码,边看边留下痕迹。
NOTE:以下所有路径为相对于源码目录下
认识系统管理服务
SystemServer源代码路径frameworks\base\services\java\com\android\server\SystemServer.javaandroid有个总的管理系统服务叫SystemServer,在此启动了系统的核心服务,比如我们这次要说的最简单的设备振子Vibrator就是在这注册进去的。如:
Slog.i(TAG, "Vibrator Service"); vibrator = new VibratorService(context); ServiceManager.addService("vibrator", vibrator);
在SystemServer.java中的main函数中先加载由c++写的各种设备JNI库libandroid_servers.so,调用init1(),然后在init1()函数中又回调了init2(),而init2实质上就是添加利用函数ServiceManager.addService()来添加各种核心服务。代码片如下:
public class SystemServer { ... native public static void init1(String[] args); public static void main(String[] args) { ... System.loadLibrary("android_servers"); init1(args); } public static final void init2() { Slog.i(TAG, "Entered the Android system server!"); Thread thr = new ServerThread(); thr.setName("android.server.ServerThread"); thr.start(); } }其中System.loadLibrary("android_servers");是调用加载JNI库libandroid_servers.so,为什么?
必须先加载JNI库(JNI相关不明白详见我的系列文章《HAL/JNI简明笔记》),后面的要添加的各种service才能找到各种硬件设备的native方法,后者依赖前者,是硬件函数C到JAVA的传递接合。
那么这个JNI库有哪些组成?
frameworks\base\services\jni\这个目录下的所有c++文件打包成的,每个c++文件都会注册到具体的java类中,比如com_android_server_VibratorService.cpp提供了vibratorExists,vibratorOn,vibratorOff三个native方法,它会被注册到com.android.server.VibratorService这样的class中,并且只有这个class才能调用者3个native方法。
init1()居然是个native函数,那就是用c++写的嘛。那应该在上面说的JNI库里,果不其然,在com_android_server_SystemServer.cpp中,init1()就是android_server_SystemServer_init1(),然后又去调用system_init()靠!居然又是extern
来的,source insight来帮忙,发现在frameworks\base\cmds\system_server\library\system_init.cpp,这是一个普通共享库libsystem_server.so,在JNI库的Android.mk中的LOCAL_SHARED_LIBRARIES含有libsystem_server.so,这样反正就能用就是了。那我们来看看system_init这个函数代码片如下:
extern "C" status_t system_init() { ALOGI("Entered system_init()"); ... ALOGI("System server: starting Android services.\n"); JNIEnv* env = runtime->getJNIEnv(); if (env == NULL) { return UNKNOWN_ERROR; } jclass clazz = env->FindClass("com/android/server/SystemServer"); if (clazz == NULL) { return UNKNOWN_ERROR; } jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V"); if (methodId == NULL) { return UNKNOWN_ERROR; } env->CallStaticVoidMethod(clazz, methodId); ... return NO_ERROR; }在这个函数中利用JNI API(c++代码)去调用了class为com.android.server.SystemServer,方法为init2的函数,这不就是上面public
class SystemServer中定义的 public static final void init2()嘛。
好了说白了就一句话,SystemServer调用init1,init1有回调了init2。
以vibrator为例说明
1)内核驱动路径:kernel\drivers\platform\msm\qpnp-vibrator.c
所在镜像boot.img
内核提供硬件,这里要么提供/dev/...或者/sys/...等,此处不详说,实际上就是让内核vibrator驱动导出一个结点/sys/class/timed_output/vibrator/enable
这个驱动注册的设备是timed_output_dev
2)硬件抽象层(HAL)
HAL路径:hardware\libhardware_legacy\vibrator\vibrator.c
所在镜像system.img中的库/system/lib/libhardware_legacy.so
这个代码就是去读写enable,来组成3个函数vibrator_exists、vibrator_on、vibrator_off。
放在hardware\libhardware_legacy下的HAL编译成的libxax.so是普通意义的库,而在其他目录下编译成的库是利用hardware.c中定义的hw_device_t格式来定制的并编译成libxbx.so库,实际上它不算普通意义上的完整库,它是借尸so的stub,它和hardware.c编译成的libhardware.so共同组成普通意义上的库,因为只有后者才能用前者,JNI并不能直接用stub库。
3)JNI(java native interface)
从这开始代码是framework部分了
JNI路径:frameworks\base\services\jni\com_android_server_VibratorService.cpp
所在镜像system.img中的库/system/lib/libandroid_servers.so
从这个源码文件名就可以看出来c函数导出给java中的哪个包哪个类使用的了,难道不是么?!这3个导出函数为vibratorExists、vibratorOn、vibratorOff,要注意c和java命名的习惯。同样为什么JNI可以调用HAL函数,难道只因为include头文件?是因为JNI的Android.mk指定了LOCAL_SHARED_LIBRARIES := \libhardware_legacy
\
4)service native
源码路径:frameworks\base\services\java\com\android\server\VibratorService.java
既然JNI中把那3个函数注册给该类使用,那么VibratorService.java中需要native声明如下:
native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
native static void vibratorOff();
这样VibratorService类就可以直接使用这3个函数了。
这个类extends IVibratorService.Stub说明是继承了IVibratorService,这个文件是编译时由IVibratorService.aidl自动生成的(见下一步骤5),IVibratorService.java存放于out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\src\core\java\android\os,可以称为service
proxy???
VibratorService.java和IVibratorService.java实现相同的接口IVibratorService.aidl。
5)AIDL(Android Interface Definition Language)
源码路径:frameworks\base\core\java\android\os\IVibratorService.aidl
源码内容也很简单如下:
package android.os; /** {@hide} */ interface IVibratorService { boolean hasVibrator(); void vibrate(int uid, String packageName, long milliseconds, IBinder token); void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, IBinder token); void cancelVibrate(IBinder token); }在VibratorService.java还有含有public void systemReady(),在IVibratorService.aidl并没有记录,说明并不是所有的public函数都得导出到google
API,按需导出即可。那么这个systemReady()给谁用呢?答案就在SystemServer.java中添加设备service后调用,表示系统准备好,进入app process。
这个aidl文件有什么用?
对于源码中的类如果你想把其中的某些接口导出去给应用开发者用(常用于新接口或者系统隐藏接口),提供接口者事先根据需要将函数接口放入到IVibratorService.aidl(与提供接口的代码在一个包),然后应用开发者需要在apk源码中引入这个aidl文件,eclipse编译后会在gen目录下生成IVibratorService.java,提供接口者将这个生成的代码放入到IVibratorService.aidl一起编译。这样接口在新烧写的系统就能和应用开发就能打通。
实际上我们需要的是生成后的IVibratorService.java,这时你将IVibratorService.aidl删掉,编译都没问题的。
6)添加service到ServiceManager
源码路径:frameworks\base\services\java\com\android\server\SystemServer.java
从有了IVibratorService.java,有了跨进程调用,导出的函数就不存在权限问题了,下面就是框架的问题了。
上面介绍SystemServer时说道init2,这个函数就是启动一个thread用于添加各种核心服务,其中vibrator服务添加如下:
Slog.i(TAG, "Vibrator Service"); vibrator = new VibratorService(context); ServiceManager.addService("vibrator", vibrator);然后当系统准备好进入app时,调用systemReady即可:
try { vibrator.systemReady(); } catch (Throwable e) { reportWtf("making Vibrator Service ready", e); }其它服务的添加与此类似。
7)Manager
我们暂且称为manager,因为其它服务命名大多含有单词manager(比如UsbManager,TelephonyManager),此处为硬件服务封装的最后一层类。
源码路径:frameworks\base\core\java\android\os\SystemVibrator.java
SystemVibrator类是继承于抽象类Vibrator,前者的构造函数中调用了
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
这样就获取了VibratorService服务,SystemVibrator.java源码中除了跟IVibratorService.aidl相同的函数,其他函数均为{@hide},也就是说不公开。
8)google API化
源码路径:frameworks\base\core\java\android\app\ContextImpl.java
这其中有个satic 括起来的部分,用于注册硬件服务,此后API可以直接使用,代码片如下
static{ ... registerService(VIBRATOR_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new SystemVibrator(ctx); }}); ... }
这样之后你在apk中就可以使用getSystemService(Service.VIBRATOR_SERVICE)来获取硬件服务,就可以控制vibrator了。
常用服务如下,具体见context.java
传入的Name | 返回的对象 | 说明
WINDOW_SERVICE WindowManager 管理打开的窗口程序
LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view
ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态
POWER_SERVICE PowerManger 电源的服务
ALARM_SERVICE AlarmManager 闹钟的服务
NOTIFICATION_SERVICE NotificationManager 状态栏的服务
KEYGUARD_SERVICE KeyguardManager 键盘锁的服务
LOCATION_SERVICE LocationManager 位置的服务,如GPS
SEARCH_SERVICE SearchManager 搜索的服务
VEBRATOR_SERVICE Vebrator 手机震动的服务
CONNECTIVITY_SERVICE Connectivity 网络连接的服务
WIFI_SERVICE WifiManager Wi-Fi服务
TELEPHONY_SERVICE TeleponyManager 电话服务
理解图
这些图摘自网络,可能会便于部分理解Service Manager 的工作就是登记功能。服务通过add_service方法将自己的名字和Binder 标识handle 登记在svclist 中。而服务请求者,通过check_service方法,通过服务名字在service list 中获取到service 相关联的Binder 的标识handle,通过这个Handle
作为请求包的目标地址发起请求。
通讯:IPC。Android 设计者在Linux内核中设计了一个叫做Binder 的设备文件,专门用来进行Android 的数据交换。从数据流来看Java 对象从Java 的VM 空间进入到C++空间进行了一次转换,并利用C++空间的函数将转换过的对象通过driver\binder 设备传递到服务进程,从而完成进程间的IPC。
(1)从JVM 空间传到c++空间,这个是靠JNI 使用ENV 来完成对象的映射过程。
(2)从c++空间传入内核Binder 设备,使用ProcessState 类完成工作。
(3) Service 从内核中Binder 设备读取数据。
由于我们在写一个Service时,在一个Package中写了Service
Native部分和Service Proxy部分,而Native和Proxy都实现相同的接口:IXXX
Interface,但是一个在服务端,一个在客服端。客户端调用的方式是使用remote->transact方法向Service发出请求,而在服务端的OnTransact中则是处理这些请求。所以在Android
Client空间就看到这个效果:只需要调用代理对象方法就达到了对远程服务的调用目的,实际上这个调用路径好长好长。
PS:怪不得android比IOS慢的??!!
其它提供接口方案
如果我们自己写个JNI库libxxx.so,那么我们java代码可以直接调用System.loadLibrary(xxx)来加载;,这就是系统定制话题了。多说句,如果你要定制是xxx.jar,那么这个xxx.jar不要调用含有系统权限或者隐藏API函数,因为普通权限应用引用这样的包,还是要签名或者获得系统权限的,不然你可能都安装不了,当然系统源码编译的当然没问题的。那么普通应用要调用隐藏API咋办?我的解决办法:一、广播;二、写AIDL服务;三、反射(网上有的能成功,有的还是不行)。
常见代码路径聚集地
1)硬件服务JNI库(C++)frameworks\base\services\jni打包为libandroid_servers.so
2)硬件service(java)
frameworks\base\services\java\打包为services.jar
3)aidl
frameworks\base\core\java\android\os
4)系统命令
frameworks\base\cmds比如上面的system_server
相关文章推荐
- cordova android 环境配置
- Android高级部分常见问题
- Android studio第一次使用配置
- [Android L or M ]解除SwitchPreference与Preference的绑定事件
- 添加背景音乐
- android studio教程及android环境搭建
- [Android L]关于Android L的Service启动问题
- Android Studio 快捷键整理分享
- Android实现音乐后台播放
- Android中系统存储 Settings
- Android IOS WebRTC 音视频开发总结(三一)-- 自定义传输实现一对多
- Android ListView使用详解
- 暑假计划
- Android中数据库的创建和使用
- Android Studio中使用volley请求数据后返回数据显示乱码的问题
- Android的v7包中的新控件——(二)RecyclerView(超级 listview,传说会代替listview)
- Android闹钟程序
- Android 官方百分比适配库
- Android之ScrollView嵌套ListView冲突
- 谈谈Android 程序框架设计