1) libhardware_legacy
是將 .so 文件當作shared library來使用,在runtime(JNI 部份)以 direct function call 使用 HAL module。通過直接函數調用的方式,來操作驅動程序。當然,應用程序也可以不需要通過 JNI 的方式進行,直接加載 .so (dlopen)的做法調用*.so 裏的符號(symbol)也是一種方式。總而言之是沒有經過封裝,上層可以直接操作硬件。
2) libhardware
HAL stub 是一種代理人(proxy)的概念,stub 雖然仍是以 .so檔的形式存在,但HAL已經將 .so 檔隱藏起來了。
這些硬件模塊都編譯成xxx.xxx.so,目標位置爲/system/lib/hw目錄
上層僅僅鏈接libhardware.so, 調用hw_get_modules 獲取相應的模塊,加載相應的庫。
1. HAL 硬件抽象層規範
HAL_MODULE_INFO_SYM
hw_module_t
HARDWARE_MODULE_TAG
1) 所有的hal模塊都要有一個以HAL_MODULE_INFO_SYM命名的結構,而且這個結構要以hw_module_t開始,即要繼承hw_module_t這個結構,比如sensor:
struct hw_module_t HAL_MODULE_INFO_SYM={};
2) 實例變量名必須爲HAL_MODULE_INFO_SYM,tag也必須爲HARDWARE_MODULE_TAG.
struct sensors_module_t {
struct hw_module_t common;
int (*get_sensors_list)(struct sensors_module_t* module,
struct sensor_t const** list);
};
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
/** major version number for the module */
uint16_t version_major;
/** minor version number of the module */
uint16_t version_minor;
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods; //硬件模塊的方法
/** module's dso */
void* dso;
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
} hw_module_t;
hw_module_t中比較重要的是硬件模塊方法結構體hw_module_methods_t定義如下:
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;
sensors_module_t,hw_module_t 不能定義爲const,否則hardware.c hmi->dso=handley運行時會出現崩潰
dso就是dlopen動態庫的句柄.
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,
};
3.提供給jni的接口 open
int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
其中調用的設備結構體參數hw_device_t定義
每一個設備的數據結構都必須也以hw_device_t開始。
struct sensor_poll_device_t{
struct hw_device_t common;
function XXXX}
struct light_device_t
{struct hw_device_t common;
function XXXX}
這些結構需要被上層服務jni函數調用。
com_android_server_lightService.cpp
SensorDevice.cpp
2. HAL訪問硬件設備
比如硬件設備是 “/dev/hello”。由於設備文件是在內核驅動裏面通過device_create創建的,而device_create創建的設備文件默認只有root用戶可讀寫,
而hello_device_open一般是由上層APP來調用的,這些APP一般不具有root權限,這時候就導致打開設備文件失敗
會出現
Hello Stub: failed to open /dev/hello -- Permission denied.
解決辦法是類似於Linux的udev規則,打開Android源代碼工程目錄下,進入到system/core/rootdir目錄,裏面有一個名爲ueventd.rc文件,往裏面添加一行:
/dev/hello 0666 root root
3.JNI的調用實現
1.上層通過hw_get_module 獲取模塊
hw_get_module函數聲明如下:
int hw_get_module(const char *id, const struct hw_module_t **module);
2. module->methods->open()獲取到具體的設備。
3.module可以定義自己的函數供上層調用。 比如
mSensorModule->get_sensors_list();
參數id爲模塊標識,定義在/hardware/libhardware/include/hardware錄下的硬件模塊頭文件中,參數module是硬件模塊地址,定義在/hardware/libhardware/include/hardware/hardware.h中
在lights.h中定義有lights模塊的ID
#define LIGHTS_HARDWARE_MODULE_ID "lights"
frameworks/base/services/jni/com_android_server_LightsService.cpp
static jint 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);
………………………………………….
}
4 .新增服務的JNI接口
1. 新建JNI接口文件
進入到系統的JNI目錄中:frameworks/base/services/jni 在這個目錄中包含了系統服務的所有JNI實現的程序
新建com_android_server_XXXService.cpp
在com_android_server_XXXService.cpp文件中,實現JNI方法。注意文件的命名方法,com_android_server前綴表示的是包名, (注意名字的唯一性)
表示硬件服務XXXService是放在frameworks/base/services/java目錄下的com/android/server目錄,即存在一個命名爲com.android.server.XXXService的類。
init函數中調用hw_get_module獲取HAL層的接口
2.添加文件到Android.mk
在同一目錄中有一個Android.mk文件,需要添加我們的這個服務,後續要將這個服務編譯到源碼中
3.添加到onload.cpp
onload.cpp程序,是系統啓動的時候去運行,內部是專門註冊系統服務的JNI方法的
定義在com_android_server_XXX.cpp中
在jniRegisterNativeMethods函數中,第二個參數的值必須對應Service所在的包的路徑,即com/android/server/XXXService。
int register_android_server_LightsService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LightsService",
method_table, NELEM(method_table));
}
JNI_OnLoad與JNI_OnUnload
framework/base/services/jni生成libandroid_servers.so
framework/base/services/java/com/android/server/systemserver.java
會運行loadlibrary
在Android中,當程序在java層運行System.loadLibrary(“jnitest”);這行代碼後,程序會去載入libjnitest.so文件,與此同時,產生一個”Load”事件,這個事件觸發後,程序默認會在載入的.so文件的函數列表中查找JNI_OnLoad函數並執行
添加AIDL接口
調用硬件服務的應用程序與這些硬件服務之間的通信需要通過代理來進行。爲此,我們要先定義好通信接口。
進入到frameworks/base/core/java/android/os目錄,新增IHelloService.aidl接口定義文件:
package android.os;
interface IHelloService {
void setVal(int val);
int getVal();
}
就會根據IHelloService.aidl生成相應的IHelloService.Stub接口。
進入到frameworks/base/services/java/com/android/server目錄,新增HelloService.java文件
package com.android.server;
import android.content.Context;
import android.os.IHelloService;
import android.util.Slog;
public class HelloService extends IHelloService.Stub {
private static final String TAG = "HelloService";
HelloService() {
init_native();
}
public void setVal(int val) {
setVal_native(val);
}
public int getVal() {
return getVal_native();
}
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
修改同目錄的SystemServer.java文件,在ServerThread::run函數中增加加載HelloService的代碼
try {
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}