Android硬件访问框架
本文内容总结由本人总结,转载请声明。
前言
本文将介绍android 上层应用访问硬件整个流程的框架,去除繁枝杂叶,从app到驱动整个流程,从上到下讲解。
android操作硬件的基本流程如下
app
通过jni
接口 loadLibrary
加载了c库
,通过调用c库的函数,进而操作到驱动。
正常到这里已经能使用了,android 为了统一就推出了jni接口标准文件
,流程图就变成:
有了jni标准接口文件
之后,我们只需要根据接口编写native层
的代码,不需要修改上层的接口。
看到这里我们是不是觉得这架构已经很完善了? 其实不然。
我们有没有思考过当有多个应该需要操作硬件的时候,如果都通过jni去范围那就有可能会导致冲突。例如多个应用播放声音,就会导致播放出来的声音很混乱。
这时候就需要一个统一接口人
来汇总,android 的做法是所有应用通过这个服务的根据优先级等策略选择汇总之后通过jni来访问硬件。
那么操作的流程图就变成:
大家可以从上面看到 SystemServer
,开发android的都知道这是android系统一个十分重要的进程,肩负着启动其他服务的作用。那到底这样的意义何在,看完下文,就能了解它的本质意思。
接下来我们就针对这个流程图做详细的扩展解说。
第一:
SystemServer
构造其他硬件访问服务并添加到系统中
1.创建对应的服务
public class LightsService extends SystemService {
.....
private static native long init_native();
private static native void finalize_native(long ptr);
static native void setLight_native(long ptr, int light, int color, int mode,
int onMS, int offMS, int brightnessMode);
}
在SystemServer
的子类LightsService
中声明了很多个native函数,并使用,这几个函数的具体实现是在哪里呢?第二点会揭晓。
2.将服务加到系统中
SystemServer.java
private SystemServiceManager mSystemServiceManager;
//把灯光操作服务加入到系统服务列表中
mSystemServiceManager.startService(LightsService.class);
//实际就是把新的服务加入到服务列表中去
private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
mServices.add(service);
上面的操作就是SystemServer
服务器将LightsService
服务添加到SystemServiceManager
服务管理器中
SystemServiceManager
管理着系统中的java层的服务
例如:BatteryService
owerManagerService
等
第二:
SystemServer
加载标准的jni接口文件
SystemServer.java
private void run() {
...
System.loadLibrary("android_servers"); //加载C库
...
}
这个c库是在源码的哪里呢?
可以看到这个c库包含了下面的文件:
重点在onload.cpp
文件,当java层调用loadLibrary
时,native会调用JNI_OnLoad
函数
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputApplicationHandle(env);
register_android_server_InputWindowHandle(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
..... //省略了若干行
register_android_server_PersistentDataBlockService(env);
register_android_server_fingerprint_FingerprintService(env);
register_android_server_Watchdog(env);
return JNI_VERSION_1_4;
}
在这个函数中调用的不同功能的Jni标准接口文件中的注册接口函数,例如 :
com_android_server_lights_LightsService.cpp
//上面截图中的问题,也就是在 android_servers.so
库中
static JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
{ "setLight_native", "(JIIIIII)V", (void*)setLight_native },
};
int register_android_server_LightsService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",
method_table, NELEM(method_table));
}
把com_android_server_lights_LightsService.cpp
文件中接口暴露给上层使用(类名为 com.android.server.lights.LightService
),关于jni的具体使用做法,不熟悉的可以参考我另外的文章。
到目前为止,我们上层的的LightsServer.java
调用类中声明的native就能直接调用到了com_android_server_lights_LightsService.cpp
的实际函数了
第三:
JNI标准接口文件调用hal层接口
static light_device_t* get_device(hw_module_t* module, char const* name)
{
int err;
hw_device_t* device;
err = module->methods->open(module, name, &device); //通过传入不同名字获得对应的设备
if (err == 0) {
return (light_device_t*)device;
} else {
return NULL;
}
}
static jlong init_native(JNIEnv *env, jobject clazz)
{
int err;
hw_module_t* module;
Devices* devices;
devices = (Devices*)malloc(sizeof(Devices));
err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
devices->lights[LIGHT_INDEX_BACKLIGHT]= get_device(module, LIGHT_ID_BACKLIGHT);
devices->lights[LIGHT_INDEX_KEYBOARD]= get_device(module, LIGHT_ID_KEYBOARD);
devices->lights[LIGHT_INDEX_BUTTONS]= get_device(module, LIGHT_ID_BUTTONS);
devices->lights[LIGHT_INDEX_BATTERY] = get_device(module, LIGHT_ID_BATTERY);
devices->lights[LIGHT_INDEX_NOTIFICATIONS] = get_device(module, LIGHT_ID_NOTIFICATIONS);
devices->lights[LIGHT_INDEX_ATTENTION] = get_device(module, LIGHT_ID_ATTENTION);
devices->lights[LIGHT_INDEX_BLUETOOTH]= get_device(module, LIGHT_ID_BLUETOOTH);
devices->lights[LIGHT_INDEX_WIFI] = get_device(module, LIGHT_ID_WIFI);
} else {
memset(devices, 0, sizeof(Devices));
}
return (jlong)devices;
}
hw_get_module
作用是加载名为LIGHTS_HARDWARE_MODULE_ID
的so库,到最后实际是使用dlopen
函数打开so库
int hw_get_module(const char *id, const struct hw_module_t **module) hardware.c
....
void* dlopen(const char* filename, int flag)
附加:dlopen
函数介绍
在dlopen()
函数以指定模式打开指定的动态链接库文件。并返回一个句柄给 dlsym()
的调用进程。
使用 dlclose(
)来卸载打开的库。
功能:打开一个动态链接库,并返回动态链接库的句柄
包括头文件:
#include <dlfcn.h>
函数定义:
void * dlopen( const char * pathname, int mode);
函数描写叙述:
mode
是打开方式,其值有多个,不同操作系统上实现的功能有所不同,
第四:
hal层接口调用驱动
static struct hw_module_methods_t lights_module_methods = {
.open = open_lights,
};
/*
* The lights Module
*/
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = LIGHTS_HARDWARE_MODULE_ID,
.name = "lights Module",
.author = "Google, Inc.",
.methods = &lights_module_methods,
};
第三步调用module的open函数会调到open_lights函数
hal层会在对灯进行一个封装,转换成逻辑灯
#define LIGHT_ID_BACKLIGHT "backlight"
#define LIGHT_ID_KEYBOARD "keyboard"
#define LIGHT_ID_BUTTONS "buttons"
#define LIGHT_ID_BATTERY "battery"
#define LIGHT_ID_NOTIFICATIONS "notifications"
#define LIGHT_ID_ATTENTION "attention"
然后根据需求操作实际的RGB灯。
第五:
上面讲了从对应的硬件服务到通过驱动操作硬件这整条路,其实为了讲解的逻辑性和可理解性前面我略了一个重要环节,就是app是怎么调用到对应服务的接口的。
1.app拿到对应的服务
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
2,将服务传给PowerManager
final IPowerManager mService;
public PowerManager(Context context, IPowerManager service, Handler handler) {
mContext = context;
mService = service;
mHandler = handler;
}
3.PowerManager
通过aidl 文件调用PowerManagerServer.java
里面的函数
这里涉及到一个很重要的文件IPowerManager.aidl
通过aidl通信就能直接调用PowerManagerServer.java
对应的函数
package android.os;
import android.os.WorkSource;
/** @hide */
interface IPowerManager
{
void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
String historyTag);
void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,
int uidtoblame);
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockUids(IBinder lock, in int[] uids);
oneway void powerHint(int hintId, int data);
void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag);
...
}
PowerManagerServer.java也要有对应操作
private final class BinderService extends IPowerManager.Stub {
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
String packageName, int uid) {
if (uid < 0) {
uid = Binder.getCallingUid();
}
acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null);
}
@Override // Binder call
public void powerHint(int hintId, int data) {
if (!mSystemReady) {
// Service not ready yet, so who the heck cares about power hints, bah.
return;
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
powerHintInternal(hintId, data);
}
...
}
这样通过andriod 的aidl机制就能执行具体的操作。aidl 机制实际是一种binder通信。这部分内容后续会进行总结。
app通过名称找到对应的服务,在把服务传给上层有权限操作的PowerManager
,PowerManager
通过aidl 作为binder的客户端访问PowerManagerServer
,并调用对应的函数。
整理框图:
总结
本文总结如有错误之处,欢迎指出,一起学习一起学习
–by mj