很多时间,我们在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:以下所有路径为相对于源码目录下
认识系统管理服务
SystemServer源代码路径frameworks\base\services\java\com\android\server\SystemServer.java
android有个总的管理系统服务叫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.jav
a还有含有public void systemReady(),在IVibrat
orService.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
WINDOW_SERVICE
LAYOUT_INFLATER_SERVICE
ACTIVITY_SERVICE
POWER_SERVICE
ALARM_SERVICE
NOTIFICATION_SERVICE
KEYGUARD_SERVICE
LOCATION_SERVICE
SEARCH_SERVICE
VEBRATOR_SERVICE
CONNECTIVITY_SERVICE
WIFI_SERVICE
TELEPHONY_SERVICE
理解图
这些图摘自网络,可能会便于部分理解
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 设备读取数据。
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