您的位置:首页 > 移动开发 > Android开发

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:以下所有路径为相对于源码目录下

认识系统管理服务

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.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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: