第二章:硬件访问服务(4)-HAL编写
通过前面的学习,我们知道怎么通过andriod硬件访问服务控制硬件,再次贴出框图如下
其中红色圈出部分我们已经完成,我们知道在andriod系统中,java程序是无法直接访问硬件的,他访问硬件需要service_manager.c发出请求,才能操控硬件,之前我们通过com_android_server_LedService.cpp直接访问硬件,这种方法一般不被推荐,主要有以下几点
1.单我们需要修改对硬件的操作C函数时,必须修改com_android_server_LedService.cpp,每次都要编译整个framework重新生成system.img。
2.linux是遵循GPL协议的,则会公开你的com_android_server_xxxService.cpp文件,倒是很多厂家并不想公开自己对硬件操作的代码。
那么为了解决以上问题,我们可以让com_android_server_xxxService.cpp文件。仅仅注册本地C函数,对硬件的操作我们把他封装成.so文件导入工程。这样我们以上的两个问题就得以解决了,需要修改对硬件的操作函数时,我们只需要修改.生成.so的文件即可。
源码解读
既然我们的要把对硬件操作部分分离出来,那么
1.com_android_server_xxxService.cpp:成了一个纯粹的JIN文件,主要有两个作用,向上提供本地C函数,向下加载hal文件(Hardware Abstraction Layer:硬件抽象层)并且调用HAL函数
2.HAL(Hardware Abstraction Layer:硬件抽象层):负责访问驱动程序,执行硬件操作。
在android中,com_android_server_xxxService.cpp加载hal文件,并不是直接声明函数,指定库就可以了,他内部有一套封装的方法。即调用dlopen,那么他是怎么调用dlopen的呢?
调用过程如下
hw_get_module("led")//1.模块名==>文件名hw_get_module_by_class("led",NULL)name = "led"property_get //获取属性值hw_module_exists //判断是否存在led.xxx.so//2.加载loaddlopen(filename)dlsym("HMI")//从so文件中获取HMI的hw_module_t结构体 strcmp(id,hmi->id)//判断名字是否一致(hmi->id,"led")
上述是一个大概的过程,现在进行详细讲解,我们先在源码中查看hw_get_module函数,如下
int hw_get_module(const char *id, const struct hw_module_t **module)
{return hw_get_module_by_class(id, NULL, module);
}
传入了两个参数,module为我们自己定义的hw_module_t **类型对象,调用hw_get_module_by_class,其中参数id为“led”,他可以看到其中多次调用hw_module_exists函数,他主要用来判断开发板以下3个路径下面是否存在led.xxx.so文件
1.HAL_LIBRARY_PATH 环境变量
2./vendor/lib/hw
3./system/lib/hw
那么led.xxx.so中的xxx具体是什么内容?我们查看hw_get_module_by_class中的property_get函数如下:
if (property_get(variant_keys[i], prop, NULL) == 0)
其中传入的参数variant_keys如下所示
static const char *variant_keys[] = {"ro.hardware", /* This goes first so that it can pick up a differentfile on the emulator. */"ro.product.board","ro.board.platform","ro.arch"
};
我们在开板上分别输入
rk3399_all:/ $ getprop ro.hardware
rk30board
rk3399_all:/ $ getprop ro.product.board
rk30sdk
rk3399_all:/ $ getprop ro.board.platform"
rk3399
rk3399_all:/ $ getprop ro.arch rk3399_all:/ $
故此,他查找的分别为led.rk30board.so,
led.rk30sdk .so,led.rk30sdk .so,led…so文件,如果都没有查找到,则查找led.default .so文件。那么他为什么是在
1.HAL_LIBRARY_PATH 环境变量2./vendor/lib/hw
3./system/lib/hw
下查找呢?因为在hw_module_exists函数中,有如下代码
char *hal_library_path = getenv("HAL_LIBRARY_PATH");if (hal_library_path) {---------------------------------------------------snprintf(path, path_len, "%s/%s.%s.so",HAL_LIBRARY_PATH2, name, subname);if (access(path, R_OK) == 0)---------------------------------------------------snprintf(path, path_len, "%s/%s.%s.so",HAL_LIBRARY_PATH1, name, subname);
然后调用
loaddlopen(filename)dlsym("HMI")//从so文件中获取HMI的hw_module_t结构体 strcmp(id,hmi->id)//判断名字是否一致(hmi->id,"led")
其中hw_module_t结构体会赋值给hw_get_module()传入的第二个参数。
总结:我们调用hw_get_module()函数,传入“led”,会获得一个和led相关的hw_module_t结构体,这个结构体在后续中,会多次使用。
JNF使用HAL
前面做了那么多的讲解,都是为我们的JNF使用HAL做铺垫,那么我们的JNF应该怎么使用HAL呢?
我们参考源代码com_android_server_lights_LightsService文件,在 init_native函数中,先调用hw_get_module()获得一个hw_module_t module结构体,然后把module当作实参传入到get_device,我们可以发现get_device()被多次调用,说明一个moudle可能支持多个device。get_device()实现如下(参考)
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;}
}
可以知get_device()函数中,调用传入参数module中的方法,获得一个hw_device_t结构体(前面提到过hw_module_t中应该包括多个hw_device_t),并且强制装换为light_device_t(设备自定义的结构体)类型返回,那么他为什么可以直接这样强制装换呢?我们再源码中查看light_device_t是怎么定义的,在/sdk/hardware/libhardware/include/hardware/ lights.h中可以看到如下定义:
struct light_device_t {struct hw_device_t common;int (*set_light)(struct light_device_t* dev,struct light_state_t const* state);
};
可以看到,最前面包含了struct hw_device_t ,所有能强制转化为light_device_t。
HAL实现
根据前面的分析,com_android_server_xxxService文件中,需要一个hw_module_t module结构体,并且调用module->methods->open(module, name, &device)。那我们编写HAL需要实现两点
1.实现一个名为HMI(HAL_MODULE_INFO_SYM:一个宏)的hw_module_t结构体
2.实现一个open函数,他会根据反回name一个设备自定义的结构体,这个设备自定义的结构体,第一个成员是hw_device_t结构体
还可以定义一些设备相关的成员,比如一些操控硬件函数。
下面我们开始编写led_hal.c文件,代码如下(参考hardware/libhardware/modules/vivrator/vivrator.c):
#define LOG_TAG "LedHal"#include <hardware/hardware.h>#include <cutils/log.h>#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>#include <utils/Log.h>#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>#include <hardware/led_hal.h>static int fd;static int led_close(struct hw_device_t* device)
{close(fd);return 0;
}static int led_open(struct led_device_t* dev)
{fd = open("/dev/leds_drv", O_RDWR);ALOGE("ledOpen : %d", fd);if (fd >= 0)return 0;elsereturn -1;
}static int led_ctrl(struct led_device_t* dev, int which, int status)
{int ret = ioctl(fd, which, status);ALOGE("ledCtrl : %d, %d, %d", which, status, ret);return ret;
}struct led_device_t led_dev = {.common = {.tag = HARDWARE_DEVICE_TAG,.close = led_close, },.led_open = led_open,.led_ctrl = led_ctrl,
};static int led_device_open(const hw_module_t* module, const char* id,
hw_device_t** device)
{*device = &led_dev;return 0;
}static struct hw_module_methods_t led_module_methods = {/*璇ュ嚱鏁帮紝com_android_server_LedService.cpp鏂囦欢涓皟鐢?hw_get_module鏃朵細琚皟鐢紝鍏朵富瑕佺洰鐨勬槸杩斿洖鎴戜滑鑷畾涔夌殑缁撴瀯浣擄紝渚汮NI鎿嶄綔*/.open = led_device_open,
};//浣跨敤HAL_MODULE_INFO_SYM瀹氫箟涓€涓粨鏋勪綋锛圚MI锛?
struct hw_module_t HAL_MODULE_INFO_SYM = {.tag = HARDWARE_MODULE_TAG,.id = "led",.methods = &led_module_methods,
};
led_hal.h
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>#include <hardware/hardware.h>__BEGIN_DECLSstruct led_device_t {struct hw_device_t common;int (*led_open)(struct led_device_t* dev);int (*led_ctrl)(struct led_device_t* dev, int which, int status);
};__END_DECLS#endif // ANDROID_LED_INTERFACE_H
除此之外我们还需修改com_android_server_LedService.cpp如下:
#define LOG_TAG "LedService"#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/led_hal.h>#include <stdio.h>#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>namespace android
{static led_device_t* led_device;jint ledOpen(JNIEnv *env, jobject cls){int err;hw_module_t* module;hw_device_t* device;/*1. hw_get_module*/err = hw_get_module("led", (hw_module_t const**)&module);ALOGE("native ledOpen");if(err == 0){/*2. get device: module->methods->open*/err = module->methods->open(module, NULL, &device);if (err == 0) {led_device = (led_device_t*)device;/*3. call led_open*/return led_device->led_open(led_device);} else {return -1;}}return -1; }void ledClose(JNIEnv *env, jobject cls){ALOGE("native ledClose");}jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status){ALOGE("native ledCtrl : %d, %d", which, status);return led_device->led_ctrl(led_device, which, status);}static const JNINativeMethod method_table[] = {{"native_ledOpen", "()I", (void *)ledOpen},{"native_ledClose", "()V", (void *)ledClose},{"native_ledCtrl", "(II)I", (void *)ledCtrl},};int register_android_server_LedService(JNIEnv *env){return jniRegisterNativeMethods(env, "com/android/server/LedService",method_table, NELEM(method_table));}};
该文件主要通过hw_get_module获得与led对应的module,再通过module->methods->open调用获得我们自定义的结构体struct led_device_t.
系统编译
编写完以上代码之后,我们需要编译 进系统
1. JNI上传
SDK/frameworks/base/services/core/jni/com_android_server_LedService.cpp
2. HAL上传
hardware/libhardware/include/hardware/led_hal.h
hardware/libhardware/modules/led/led_hal.c
hardware/libhardware/modules/led/Android.mk
其中没有的目录和文件就自己创建,Android.mk的内容如下
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := led.default# HAL module implementation stored in
# hw/<VIBRATOR_HARDWARE_MODULE_ID>.default.so
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := userdebuginclude $(BUILD_SHARED_LIBRARY)
该文档参考hardware/libhardware/modules/virator/Android.mk 编写,其中LOCAL_MODULE_TAGS := userdebug是因为我们在编译源码的时候选择的是lunch rk3399_all-userdebug。
然后执行编译指令
source build/envsetup.sh
lunch rk3399_all-userdebug
mmm frameworks/base/services/
mmm hardware/libhardware/modules/led/
make snod -j3
生成system.img烧写到开发板
运行APP我们局可以操控led了
打印简介
1.在android中有三类打印信息:app,system,radio,程序里使用ALOGx,SLOGx,RLOGx来打印
2.其中x代表打印级别,有如下可选:V VerboseD DebugW InfoE WarnF Fatal比如:
#define LOG_TAG "LedHal"
ALOGI("led_open" :%d",fd);3 打印出来的格式如下I/Led (1987):led_open :65(级别)LOG_TAG 进程号 打印信息
4.那我们怎么查看打印信息呢?可以在串口终端上执行logcat命令筛选想要的信息:1. logcat | grep “LedHal” 2. logcat LedHal:I *:s(该处死两层过滤,可以过滤最多层)
编译优化
我们APP需要导入自己编译生成classes.jar包,该文件接近16M左右,编译生成的app/build/outpus/apk/debug/app-arm64-v8a-debug.apk也达到了8.8M,仅仅是点亮LED却占用了这么大的空间,当然并不是我们想要的结果,那么我们怎么对其进行优化呢?
打开AS工程,选择File->Projiect Structure
点击app->Dpendencies,选择classes.jar为compile模式
然后重新编译,我么可以看到此次APK大约1M左右。
小节回顾
本小节我们讲解了一下几点:
- 参考源码com_android_server_lights_LightsService,模仿使用hw_get_module与module->methods->open实现JNI
- 参考hardware/libhardware/modules/vivrator/vivrator.c文件编写led_hal.c
- 打印调试信息相关了解
下小节将修改APP,使用反射的方式操控硬件。
第二章:硬件访问服务(4)-HAL编写相关推荐
- 第1课第4.4节_Android硬件访问服务编写HAL代码
android应用如何访问C库 - 落魄影子 - 博客频道 - CSDN.NET http://blog.csdn.net/ab198604/article/details/51249303 And ...
- Android硬件访问服务框架思想初识
Android的硬件访问服务提供了一个APP调用硬件实现的方法模型.我们从上往下来看.应用层面对的都是一个个的服务叫service.比如电源管理服务,震动服务等等.应用层代码首先就需要去查询系统是否存 ...
- android+硬件服务,android之硬件访问服务框架
一.硬件接口描述文件aidl 新增\frameworks\base\core\java\android\os\ILedService.aidl 二.mk编译脚本 修改vi frameworks/bas ...
- 嵌入式Android底层开发(三)硬件访问服务框架
一.简单的Android应用程序调用硬件流程图(通过JNI接口直接访问) 但这样存在一个很大的问题:访问仅限与单一程序进行访问,如果多个APP同时访问一个硬件资源(多个APP都需要在LCD进行显示.多 ...
- Android系统硬件访问服务框架分析
怎么实现硬件访问服务 1.JNI和HAL com_andorid_server_ledService.cpp (JNI文件注册JNI本地方法:供app应用程序调用) hal_led.c (C库:具体操 ...
- ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发
为什么80%的码农都做不了架构师?>>> ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发 前言:本篇主要讲述数据访问层的开发, ...
- 2.3硬件访问服务-APP修改编译
目录 APP修改 相关依赖导入 参考 3288 MainActivity build.gradl 小节回顾 通过上一小节,我们硬件访问服务已经基本完成(还有少些部分需要修改,下小节为大家讲解),该小节 ...
- 第二章 系统集成及服务管理知识点1
这第二章主要讲了下集成及服务管理的内容.制度.意义.管理办法.以及一些管理方面的服务概念.跟着小老弟把内容给归纳归纳,后面来复习的时候也能够省不少时间! 1信息系统集成及服务管理的内容 在信息化建设过 ...
- 在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务(老罗学习笔记5)...
在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行.今天大家对iOS和Android系统的趋之若鹜,一定程度上是由于这两个系统上有着丰富多彩的各种应用软件.因此,软件和硬件的关系 ...
- 《分析服务从入门到精通读书笔记》第二章、分析服务工具篇
Microsoft提供了一套完整的商业智能平台应用程序.使我们能够创建企业级.功能齐全的商业智能系统. Microsoft商业智能平台基于SQL Server 2008和Microsoft Offic ...
最新文章
- PHP安全、Sql防注入安全汇总
- Xamarin iOS项目找不到模拟器
- 【Linux】文件特殊权限 SUID/SGID/Sticky Bit
- vant图标怎么显示不出来_U盘插进电脑但不显示怎么解决
- JS数组”(array)和“对象”(object)
- Bootstrap的datatable控件
- 阿里云ECS服务器搭建wordpress个人博客网站【详细图文教程】
- C# 子类实例化基类 基类使用不了子类的方法_C#高级编程面试考题
- Android在布局XML中的空格转义符(占位符)
- cv mat 灰度值和_c++ 访问OpenCV中灰度图像的像素值
- extract-text-webpack-plugin用法
- php中echo(),print(),print_r()的区别
- 如何修改开机背光亮度
- java读取pdf的文字、图片、线条和对应坐标
- Linux——系统磁盘管理与文件系统
- ABC-Endless Walk-(缩点+拓扑dp)
- 【Blender】Blender全按键解析 01(目前咕咕咕了)
- 无法直接在IE中打开PDF文件的解决方法
- js的match 方法
- matlab 进行非线性回归,5.利用Matlab编程进行非线性回归分析.doc
热门文章
- ansible(一)自动化运维工具
- 2008服务器系统备份工具,服务器2008系统备份
- Homestead GuestAdditions seems to be installed (6.0.6) correctly, but not running
- ES6(十八)Module
- chrome如何对部分网页截图
- 王老吉、加多宝双茶争霸:新零售布局将成未来胜负关键?
- Linux优化学习之Load Average (平均负载)
- load average 计算
- 网管系统测试学习——传输网下一代综合网管系统测试
- oracle中间人投毒漏洞,‘TNS Listener’远程数据投毒漏洞