欢迎转载,务必注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44305599
本篇文章记录硬件抽象层。
还是跟之前一样,主要参考《Android系统源码情景分析》。
书里面写的是在/hardware/libhardware目录下写硬件抽象层,我这里并没有在该目录下,因为我使用的是与板子相关的,所以我就放在了板级目录下了,路径为/device/nexell/realarm,在/device/nexell/realarm路径下建立一个led文件夹来存放需要的.c、.h、.mk文件,我的文件名为led.c、led.h、Android.mk,源码分别如下:
led.c:
#include
#include "led.h"
#include
#include
#include
#include
// 引入log头文件
#include
// log标签
#define TAG "Led_Load_HAL"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG,__VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#define DEVICE_NAME "/dev/real_led"
#define MODULE_NAME "led"
#define MODULE_AUYHOR "[email protected]"
#define LED_ON 1
#define LED_OFF 0
static int led_device_open(const struct hw_module_t *module, const char *id, struct hw_device_t **device);
static int led_device_close(struct hw_device_t *device);
static int led_set_on(struct led_device_t *dev, int num);
static int led_set_off(struct led_device_t *dev, int num);
static struct hw_module_methods_t led_module_methods = {
open: led_device_open
};
struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID,
name: DEVICE_NAME,
author: MODULE_AUYHOR,
methods: &led_module_methods,
}
};
static int led_device_open(const struct hw_module_t *module, const char *id, struct hw_device_t **device) {
if(!strcmp(id, LED_HARDWARE_DEVICE_ID)) {
struct led_device_t *dev;
dev = (struct led_device_t *)malloc(sizeof(struct led_device_t));
if(!dev) {
LOGE("Failed to alloc space for led_device_t");
return -EFAULT;
}
memset(dev, 0, sizeof(struct led_device_t));
dev->common.tag = HARDWARE_MODULE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t *)module;
dev->common.close = led_device_close;
dev->set_on = led_set_on;
dev->set_off = led_set_off;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
LOGE("Failed to open device file "DEVICE_NAME"-- %s.", strerror(errno));
free(dev);
return -EFAULT;
}
*device = &(dev->common);
LOGI("Open device file "DEVICE_NAME" successfully.");
return 0;
}
return -EFAULT;
}
static int led_device_close(struct hw_device_t *device){
struct led_device_t *led_device = (struct led_device_t *)device;
if(led_device){
close(led_device->fd);
free(led_device);
}
return 0;
}
static int led_set_on(struct led_device_t *dev, int num){
if(!dev){
LOGE("Null dev pointer.");
return -EFAULT;
}
LOGI("Set the first %d LED lights.", num);
ioctl(dev->fd, LED_ON, num);
return 0;
}
static int led_set_off(struct led_device_t *dev, int num){
if(!dev){
LOGE("Null dev pointer.");
return -EFAULT;
}
LOGI("Set the first %d LED close.", num);
ioctl(dev->fd, LED_OFF, num);
return 0;
}
led.h:
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H
#include
__BEGIN_DECLS
#define LED_HARDWARE_MODULE_ID "led"
#define LED_HARDWARE_DEVICE_ID "led"
/*自定义模块结构体*/
struct led_module_t {
struct hw_module_t common;
};
/*自定义设备结构体*/
struct led_device_t {
struct hw_device_t common;
int fd;
int (*set_on)(struct led_device_t *dev, int num);
int (*set_off)(struct led_device_t *dev, int num);
};
__END_DECLS
#endif
Android.mk:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := led.c
LOCAL_SHARED_LIBRARIES := liblog
#LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_MODULE := led.default
include $(BUILD_SHARED_LIBRARY)
上面三个文件生成的是.so文件,该硬件抽象层为JNI方法提供接口。
mmm ./device/nexell/realarm/led/
在执行上面的mmm命令时,先确保已经把Android源码的环境变量已经添加进系统,也就是是否执行过source ./build/envsetup.sh 这条命令了。
另外在使用mmm指令时,如果没有使用lunch命令选择过编译的板级目标,那么一般默认的可能不是需要的板级目标,所以要使用lunch命令进行选择一下,如下所示:
wsh@ubuntu:/wsh_space/nexell/s5p4418/debug/android/android$ lunch
You're building on Linux
Lunch menu... pick a combo:
1. aosp_arm-eng
2. aosp_x86-eng
3. aosp_mips-eng
4. vbox_x86-eng
5. mini_x86-userdebug
6. mini_mips-userdebug
7. mini_armv7a_neon-userdebug
8. aosp_manta-userdebug
9. aosp_drone2-userdebug
10. aosp_drone-userdebug
11. aosp_realarm-userdebug
12. aosp_grouper-userdebug
13. aosp_deb-userdebug
14. aosp_flo-userdebug
15. aosp_tilapia-userdebug
16. aosp_hammerhead-userdebug
17. aosp_mako-userdebug
Which would you like? [aosp_arm-eng]
硬件访问服务接口一般是在/frameworks/base/core/java/android/os目录下定义。我这里的文件名是ILedService.aidl,源码如下:
package android.os;
interface ILedService{
int seton(int num);
int setoff(int num);
}
为了能够编译它需要修改
/frameworks/base/目录下的Android.mk文件,添加一行代码:core/java/android/os/ILedService.aidl \
packages/services/Proxy/com/android/net/IProxyCallback.aidl \
packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
core/java/android/os/ILedService.aidl \
然后使用下面命令进行编译:
mmm ./frameworks/base/
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import android.content.Context;
import android.os.ILedService;
import android.util.Slog;
/**
* Shared singleton foreground thread for the system. This is a thread for regular
* foreground service operations, which shouldn't be blocked by anything running in
* the background. In particular, the shared background thread could be doing
* relatively long-running operations like saving state to disk (in addition to
* simply being a background priority), which can cause operations scheduled on it
* to be delayed for a user-noticeable amount of time.
*/
public class LedService extends ILedService.Stub {
private static final String TAG = "LedService";
private int mPtr = 0;
LedService(){
mPtr = init_native();
if(mPtr == 0){
Slog.e(TAG, "Failed to initialize Led service.");
}
}
public int setOn(int num){
if(mPtr == 0){
Slog.e(TAG, "Led service is not initialize.");
return 0;
}
setOn_native(mPtr, num);
return 0;
}
public int setOff(int num){
if(mPtr == 0){
Slog.e(TAG, "Led service is not initialize.");
return 0;
}
setOff_native(mPtr, num);
return 0;
}
private static native int init_native();
private static native int setOn_native(int ptr, int num);
private static native int setOff_native(int ptr, int num);
};
由上面代码可知,实现了硬件服务接口setOn和setOff的具体方法,由于java不能够直接使用HAL层提供的接口,所以这里使用native方式来与JNI方法连接。
mmm ./frameworks/base/services/java/
编译得到的server.jar就包含了LedService类。
目录为/frameworks/base/services/jni,文件命名为com_android_server_LedService.cpp,要注意命名格式,一般为com_android_server_xxx.cpp。
源码如下:
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include
#include
#include
#include "../../device/nexell/realarm/led/led.h"
#include
// 引入log头文件
#include
// log标签
#define LOG_TAG "LedServiceJNI"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
namespace android
{
static jint led_seton(JNIEnv *env, jobject clazz, jint ptr, jint number){
led_device_t *device = (led_device_t *)ptr;
if(!device){
LOGE("Device led is not open");
return 0;
}
int num = number;
LOGI("Set the first %d LED lights.", num);
device->set_on(device, num);
return num;
}
static jint led_setoff(JNIEnv *env, jobject clazz, jint ptr, jint number){
led_device_t *device = (led_device_t *)ptr;
if(!device){
LOGE("Device led is not open");
return 0;
}
int num = number;
LOGI("Set the first %d LED close.", num);
device->set_off(device, num);
return num;
}
static inline int led_device_open(const hw_module_t *module, struct led_device_t **device){
return module->methods->open(module, LED_HARDWARE_DEVICE_ID, (struct hw_device_t**)device);
}
static jint led_init(JNIEnv *env, jobject clazz){
led_module_t *module;
led_device_t *device;
LOGI("Initializing HAL stub led......");
if(hw_get_module(LED_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0){
LOGI("Device led found");
if(led_device_open(&(module->common), &device) == 0){
LOGI("Device led is open.");
return (jint)device;
}
LOGE("Failed to open device led.");
return 0;
}
LOGE("Failed to get HAL stub led.");
return 0;
}
static const JNINativeMethod method_table[] = {
{"init_native", "()I", (void*)led_init},
{"setOn_native", "(II)I", (void*)led_seton},
{"setOff_native", "(II)I", (void*)led_setoff},
};
int register_android_server_LedService(JNIEnv *env){
return jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table));
}
};
另外还需要修改两个文件。
修改/frameworks/base/services/jni/onload.cpp,完整文件如下:
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_LedService(JNIEnv *env);//user add
};
using namespace android;
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_AlarmManagerService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbHostManager(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_location_FlpHardwareProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_AssetAtlasService(env);
register_android_server_ConsumerIrService(env);
register_android_server_LedService(env);//user add
return JNI_VERSION_1_4;
}
添加了int register_android_server_LedService(JNIEnv *env);//user add和register_android_server_LedService(env);//user add。
修改/frameworks/base/services/jni/Android.mk文件,添加如下:
LOCAL_SRC_FILES:= \
......
com_android_server_LedService.cpp \
onload.cpp
mmm ./frameworks/base/services/jni/
该部分的目的是让系统在启动的时候就加载led服务。
修改目录/frameworks/base/services/java/com/android/server/目录下的SystemServer.java文件,如下所示:
ActivityManagerService.self().systemReady(new Runnable() {
public void run() {
......
//user add
try {
Slog.i(TAG, "Realarmled service");
ServiceManager.addService("led", new LedService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Realarmled Service", e);
}
}
让系统在启动的时候加载LedService服务。
编译:
mmm ./frameworks/base/services/java/
到这里,out/target/product/realarm/system目录下就已经包含了我们所编译后的jar和.so文件了,打包系统文件烧写到开发板,下一个博客记录怎么写配套的应用程序app。
注意:打包之前检查一下ueventd.realarm.rc这个文件的最后时候有这一句/dev/real_led 0666 systemsystem,该句是修改驱动程序生成的real_led设备节点权限,以提供给HAL使用,否则会提示没有权限,无法打开的错误。
应用程序的写法介绍用两种方式,eclipse和Android源码目录下这两种方式。这里面涉及一些库的使用题,也是最头痛的事情,怎么让eclipse能够应用我们自己的类。