添加新的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模板如下所示:
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)
LOCAL_CFLAGS := -Wno-unused-parameter
以忽略掉该报错。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
② 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");
进行打印
参考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");
system/core/init/README.md
.rc
中添加规则,实例:device/linaro/hikey/init.common.rc
以控制LED灯为背景,进行JNI编程技术的介绍。aosp中JNI技术参考代码:development/samples/SimpleJNI/
将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();
/system/app/
下作为system_app
① 定义JAVA和C/C++间的映射表如下所示。其中()I
是java中native方法的签名,在APP目录LedControl\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes
下执行javap -s com.example.lowlevel.LedNative
可查看各方法的签名,再把他们分别填充到映射表。
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
参考我的另一篇文章有详细开发步骤:Android Studio开发NDK代码。
本章介绍开发传统HAL代码和调用HAL的主要流程,如下出现的代码场景是控制LED灯。
开发HAL在头文件,定义相应硬件模块和设备结构,以继承基础模块结构(struct hw_module_t
)和基础设备结构(struct hw_device_t
)。另外还有暴露接口给调用者的目的。具体开发流程如下所示:
#ifndef __LED_HAL_H__
#define __LED_HAL_H__
#include
__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
① 实例化自己派生的专有硬件模块结构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 = "[email protected]",
.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)
;
① 动态库名应为[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
。
/vendor/lib64/hw/
和/vendor/lib/hw/
,还需加上规则LOCAL_PROPRIETARY_MODULE := true
hardware/libhardware/modules/gralloc
① 加载硬件的HAL模块,使用 int hw_get_module(const char *id, const struct hw_module_t **module)
;
id
——HAL动态库名字的前缀,本场景值为led_hal
;module
——通过此参数获得hw_module_t
实例#include
#include
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);
libhardware
,故Android.mk
有LOCAL_SHARED_LIBRARIES := libhardware
。frameworks/native/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
[1] moasm. Android Log系统介绍 (基于Android N)[EB/OL].简书,2019