基本环境
一、build
添加新的lunch选项(新产品):赋值COMMON_LUNCH_CHOICES,PRODUCT_MAKEFILES如下 (参考device/sample/products/AndroidProducts.mk,实例参考device/linaro/hikey/AndroidProducts.mk)
PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/sample_addon.mk COMMON_LUNCH_CHOICES := sample_addon-userdebug
二、编写Android.mk
Android.mk模板如下所示:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ hello.c LOCAL_MODULE_TAGS := optional LOCAL_MODULE := hello_elf_arm64 include $(BUILD_EXECUTABLE)
编译报unused parameters错时,需加上:LOCAL_CFLAGS := -Wno-unused-parameter 以忽略掉该报错。
三、LOG系统
log系统的设备节点:
hikey960:/ # ls /dev/socket/log* -l srw-rw-rw- 1 logd logd 0 1970-01-01 00:00 /dev/socket/logd srw-rw-rw- 1 logd logd 0 1970-01-01 00:00 /dev/socket/logdr s-w--w--w- 1 logd logd 0 1970-01-01 00:00 /dev/socket/logdw
转载来自文章《Android Log系统介绍 (基于Android N)》的log系统核心图如下所示,更多细节请参考此文。
图1 Android log系统
Android Q、R上面log的缓冲区有:main,system,radio,events,crash
c/c++中打印调试信息:
① 宏定义标签及添加头文件:
#define LOG_TAG "hello"
#include <log/log.h>
② Android.mk中声明所依赖的动态库:
LOCAL_SHARED_LIBRARIES := \
liblog
③ 使用宏ALOGV(),ALOGD(“xxx”),ALOGI(),ALOGW(),ALOGE(),进行打印log
java中打印log信息:
① 导入包:import android.util.Log;
② 在类内部定义标签:private final String TAG = "listview";
③ 使用方法Log.d(TAG, "xxx");,Log.i(TAG, "xxx");,Log.w(TAG, "xxx");,Log.e(TAG, "xxx");进行打印
四、init.rc
参考system/core/init/init.cpp中如下代码,可知/init祖先进程通过解析/init.rc的内容来进行启动各种本地服务
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) { Parser parser = CreateParser(action_manager, service_list); std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/init.rc");
android init语言说明文档:system/core/init/README.md
在厂商.rc中添加规则,实例:device/linaro/hikey/init.common.rc
五、JNI
以控制LED灯为背景,进行JNI编程技术的介绍。aosp中JNI技术参考代码:development/samples/SimpleJNI/
1. java调用jni接口
将native接口的声明组织到LedControl\app\src\main\java\com\example\lowlevel\LedNative.java
package com.example.lowlevel; public class LedNative { /* 1) 加载jni动态库 */ static { System.loadLibrary("led_jni"); } /* 2) 使用关键字native声明jni接口 */ public native int openDev(); public native int closeDev(); public native int devOn(); public native int devOff(); }
然后在class MainActivity中进行调用:
/* 3) 使用上面定义的LedNative类生成对象后调用接口 */ LedNative ledNative = new LedNative(); ledNative.devOn();
注意:普通app没有权限加载jni动态库依赖的库,需将apk预置或传入到手机的/system/app/下作为system_app
2. aosp中开发native代码
① 定义JAVA和C/C++间的映射表:
static JNINativeMethod ledMethod[] = { {"openDev", "()I", (void*)openLed}, {"closeDev", "()I", (void*)closeLed}, {"devOn", "()I", (void*)ledOn}, {"devOff", "()I", (void*)ledOff}, };
② 通过定义jni层的回调函数jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/),来将上述映射表注册到java虚拟机:
static const char *classPathName = "com/example/lowlevel/LedNative"; jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { jint ret; JNIEnv* env = NULL; ALOGD("------%s", __FUNCTION__); ret = vm->GetEnv((void**)&env, JNI_VERSION_1_4); if (ret != JNI_OK) { ALOGE("ERROR: GetEnv failed"); return -1; } jclass cls = env->FindClass(classPathName); if (cls == NULL) { ALOGE("Native registration unable to find class '%s'", classPathName); return JNI_FALSE; } ret = env->RegisterNatives(cls, ledMethod, sizeof(ledMethod)/sizeof(ledMethod[0])); if (ret < 0) { ALOGE("RegisterNatives failed for '%s'", classPathName); return JNI_FALSE; } return JNI_VERSION_1_4; }
③ Android.mk中指定生成的是动态库和编译工具分别如下:
LOCAL_MODULE:= libled_jni include $(BUILD_SHARED_LIBRARY)
注意:测试时,需要修改设备节点权限:# chmod 0666 /sys/devices/platform/leds/leds/user_led3/brightness,以及关闭selinux:# setenforce 0
六、传统HAL
本章介绍开发传统HAL代码和调用HAL的主要流程,如下出现的代码场景是控制LED灯。
1. 接口声明
开发HAL在头文件,定义相应硬件模块和设备结构,以继承基础模块结构(struct hw_module_t)和基础设备结构(struct hw_device_t)。另外还有暴露接口给调用者的目的。具体开发流程如下所示:
#ifndef __LED_HAL_H__ #define __LED_HAL_H__ #include <hardware/hardware.h> __BEGIN_DECLS /* 1) 定义生成的动态库前缀的名称, xxx.default.so */ #define LED_HARDWARE_MODULE_ID "led_hal" /* 2) 定义专有硬件模块,继承 hw_module_t,并扩展要暴露的模块控制接口 */ typedef struct led_module { struct hw_module_t common; } led_module_t; /* 3) 定义专有硬件硬件,继承 hw_device_t */ typedef struct led_device { struct hw_device_t common; /* 4) 扩展要暴露出来的设备控制接口 */ int (*control)(int enable); } led_device_t; /* 5) 用来调用 hw_module_t.methods->open() */ static inline int led_hal_open(const struct hw_module_t* module, led_device_t** device) { return module->methods->open(module, NULL, TO_HW_DEVICE_T_OPEN(device)); } /* 6) 用来调用 hw_device_t.close() */ static inline int led_hal_close(led_device_t* device) { return device->common.close(&device->common); } __END_DECLS #endif
2. 接口实现
① 实例化自己派生的专有硬件模块结构led_module_t;
led_module_t HAL_MODULE_INFO_SYM = { /* 1.实例名必须为 HAL_MODULE_INFO_SYM */ .common = { .tag = HARDWARE_MODULE_TAG,/* 2.基础结构标签必为 HARDWARE_MODULE_TAG */ .module_api_version = HARDWARE_MODULE_API_VERSION(1,0),/* 3.模块版本为1.0 */ .hal_api_version = HARDWARE_HAL_API_VERSION,/* 4.固定 */ .id = LED_HARDWARE_MODULE_ID,/* 5.绑定最后生成的动态库名:[id].default.so */ .name = "Hikey960 led HAl", .author = "luciferzhu@yeah.net", .methods = &led_module_methods,/* 6.指定激活模块下设备的方法,下文中介绍 */ }, };
② 实例化struct hw_module_methods_t,该结构下成员只有open;
struct hw_module_methods_t led_module_methods = { .open = led_module_open,/* 在下文中定义 */ };
③ 定义hw_module_methods_t.open(),它的任务是实例化struct hw_device_t的派生结构体
led_device_t; int led_module_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) { led_device_t* led_dev = (led_device_t*)calloc(1, sizeof(led_device_t)); /* 1.申请内存实例化派生结构 */ led_dev->common.tag = HARDWARE_DEVICE_TAG;/* 2.HAL设备标签,值固定 */ led_dev->common.version = HARDWARE_DEVICE_API_VERSION(1, 0); led_dev->common.module = (struct hw_module_t*)module; led_dev->common.close = led_device_close;/* 2.close函数与本函数任务相反,用于释放相关资源 */ led_dev->control = led_control;/* 3.对接自定义的接口 */ *device = (hw_device_t*) led_dev;/* 4.通过参数返回实例化后设备指针,HAL调用者通过此指针调用设备的控制接口 */ return 0; };
④ 定义hw_device_t.close(),HAL调用者调用此函数用于关闭设备和释放hw_module_methods_t.open()中申请的资源;
int led_device_close(struct hw_device_t* device) { led_device_t* priv = (led_device_t*) device; if (priv) free(priv); return 0; }
⑤ 实现暴露给HAL调用者的接口,本场景要实现int led_control(int enable);
3. 添加编译规则
① 动态库名应为[id].default.so,所以LOCAL_MODULE:= led_hal.default;
② HAL动态库应部署到/system/lib64/hw/和/system/lib/hw/下,需加上规则LOCAL_MODULE_RELATIVE_PATH := hw;
③ 导出暴露给调用者的接口头文件。部署到system分区的HAL库,使用LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include;部署到vendor分区的HAL库,应使用LOCAL_HEADER_LIBRARIES := libhardware_headers。
若要HAL动态库部署到/vendor/lib64/hw/和/vendor/lib/hw/,还需加上规则LOCAL_PROPRIETARY_MODULE := true
HAL参考代码:hardware/libhardware/modules/gralloc
4. 使用HAL的接口
① 加载硬件的HAL模块,使用 int hw_get_module(const char *id, const struct hw_module_t **module);
id——HAL动态库名字的前缀,本场景值为led_hal; module——通过此参数获得hw_module_t 实例 #include <hardware/hardware.h> #include <led_hal.h> static led_module_t* pModule = NULL;/* 全局变量 */ int ret = hw_get_module(LED_HARDWARE_MODULE_ID, (const struct hw_module_t **)&pModule); if (ret != 0) { ALOGE("get hardware module '%s' failed", LED_HARDWARE_MODULE_ID); return -1; }
② 通过hw_module_t 的派生结构体led_module_t,获取hw_device_t的派生结构体led_device_t,方法如下所示。但为了方便调用者,HAL接口头文件一般会将下面的代码实现到内联函数中,如调用前文的led_hal_open((const struct hw_module_t*)pModule, &pDevice);;
static led_device_t* pDevice = NULL; pModule->common.methods->open((const struct hw_module_t *)pModule, NULL, (struct hw_device_t**)&pDevice);
③使用HAL接口进行硬件控制的形式:pDevice->foo(xxx);;如本场景使用:pDevice->control(1);来点亮LED;
④ 在需要关闭设备和释放有关内存资源时,需调用hw_device_t.close(),所以调用方法如下所示。但为了方便调用者,HAL接口头文件一般会将下面的代码实现到内联函数中,如调用前文的led_hal_close(pDevice);;
pDevice->common.close((struct hw_device_t*)pDevice);
注:调用HAL动态库的代码,需要链接动态库libhardware,故Android.mk有LOCAL_SHARED_LIBRARIES := libhardware。
aosp中调用HAL的参考代码:frameworks/native/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
发表评论 取消回复