Android硬件访问框架

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

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值