Android HAL 介紹

  HAL 介紹 Android 的 HAL (硬件抽像層)是 Google 因應廠商「希望不公開源碼」的要求下,所推出的新觀念,其架構如下圖。雖然 HAL 現在的「抽象程度」還不足,現階段實作還不是全面符合 HAL 的架構規劃,不過也確實給了我們很好的思考空間。
  
  圖 1 : Android HAL 架構規劃
  這是 Patrick Brady (Google) 在 2008 Google I/O 所發表的演講「 Anatomy & Physiology of an Android 」中,所提出的 Android HAL 架構圖。從這張架構圖我們知道, HAL 的目的是爲了把 Android framework 與 Linux kernel 完整「隔開」。讓 Android 不至過度依賴 Linux kernel ,有點像是「 kernel independent 」的意思,讓 Android framework 的開發能在不考慮驅動程序的前提下進行發展。
  在 Android 原始碼裏, HAL 主要的實作儲存於以下目錄:
  1. libhardware_legacy/ - 過去的實作、採取鏈接庫模塊的觀念進行
  2. libhardware/ - 新版的實作、調整爲 HAL stub 的觀念
  3. ril/ - Radio Interface Layer
  在 HAL 的架構實作成熟前(即圖 1 的規劃),我們先就目前 HAL 現況做一個簡單的分析。另外,目前 Android 的 HAL 實作,仍舊散佈在不同的地方,例如 Camera 、 WiFi 等,因此上述的目錄並不包含所有的 HAL 程序代碼。
  HAL 的過去
  
  圖 2 : Android HAL / libhardware_legacy 過去的 libhardware_legacy 作法,比較是傳統的「 module 」方式,也就是將 *.so 檔案當做「 shared library 」來使用,在 runtime ( JNI 部份)以 direct function call 使用 HAL module 。透過直接函數呼叫的方式,來操作驅動程序。
  當然,應用程序也可以不需要透過 JNI 的方式進行,直接以加載 *.so ?( dlopen )的做法呼叫 *.so 裏的符號( symbol )也是一種方式。
  HAL 的現實狀況
  
  圖 3 : Android HAL / libhardware 現在的 libhardware 作法,就有「 stub 」的味道了。 HAL stub 是一種代理人( proxy )的概念, stub 雖然仍是以 *.so ?的形式存在,但 HAL 已經將 *.so 檔隱藏起來了。 Stub 向 HAL 「提供」操作函數( operations ),而 runtime 則是向 HAL 取得特定模塊( stub )的 operations ,再 callback 這些操作函數。這種以 indirect function call 的實作架構,讓 HAL stub 變成是一種「包含」關係,即 HAL 裏包含了許許多多的 stub (代理人)。 Runtime 只要說明「類型」,即 module ID ,就可以取得操作函數。 HAL 的實現主要在 hardware.c 和 hardware.h 文件中。實質也是通過加載 *.so 檔( dlopen )從而呼叫 *.so 裏的符號( symbol )實現。這裏所謂的代理,我感覺不過是 Android 統一定義了三個結構體,然後通過幾個"必須"從而統一了調用接口
  HAL 的未來發展?
  新的 HAL 做法,傾向全面採用 JNI 的方式進行。也就是,在 Android 的架構中,修改 Android runtime 實作(即「 Core Library 」),在取得 HAL 模塊的 operations 後再做 callback 操作。將 HAL 模塊完全放在 HAL 裏面。以上我想應該是針對 framework 開發來說的。如果僅是使用 hal 訪問硬件,完全可以不修改 core library 。
  一、 HAL 使用步驟:
  (1)Java AP 初始化一個 java service , 然後根據需求組合調用 java service 提供的接口。
  (2)Java Service 設置 Native Interface 聲明, 並在初始化時加載 Native Service 所在的庫 .Native Service 實際上是一個動態鏈接庫, 通過 JNI 和 Java Service 交互。
  (3) 通過 OnLoad 方法註冊與 Java Service 的 Native Function 之間的對應 JNI table 。
  (4) 通過 HAL Module ID 獲得當前實際板上對應的硬件設備的 Module , 並通過此 Module 的 HAL 接口 Open 獲得硬件設備的 device 實例。 通過 device 提供的接口組合本地函數的實現。
  (5) 編寫 HAL stub, 對具體的硬件設備初始化對應 Module 和 Device 實例, 並實現對硬件驅動的 API 封裝。
  (6)HAL 模塊要以 MODULE_ID.platform.so 的名字存放在文件系統的 /system/lib/hw/ 下面。
  Android HAL 層主要在 hardware 目錄下,其中 hardwarelibhardware 下是同一用模塊的概念來加載 HAL 的 .so 庫。 這裏以一個簡單的 led 小例子(假設備,不涉及硬件操作)來說明具體實現步驟。
  二、 HAL Stub 實現步驟 (Implementation)
  1. 設計自已的 wrapper data structure
  * 編寫頭文件 led.h
  * 定義 struct led_module_t
  * 框架提供的 struct hw_module_t 、 struct hw_device_t 必須 放在第一個 field ,並取名爲 common 。
  * 可參考 hardware/hardware.h
  struct led_module_t {
  struct hw_module_t common;
  /* support API for LEDServices constructor */
  };
  2. led_module_t 的意義
  聲明初始化時期 (new object) 的 supporting API 、在 constructor 裏會使用到。
  3. 定義 led_control_device_t
  聲明控制時期的 supporting API 、在 Manager API 裏會使用到。
  struct led_control_device_t {
  struct hw_device_t common;
  /* supporting control APIs go here */
  int (*getcount_led)(struct led_control_device_t *dev);
  int (*set_on)(struct led_control_device_t *dev);
  int (*set_off)(struct led_control_device_t *dev);
  };
  4. 每? HAL stub 都要聲明 module ID
  #define LED_HARDWARE_MODULE_ID "led"
  5. 聲明 Stub operations 並實現 callback functions
  Stub 的 module 結構體必須 取名爲 HAL_MODULE_INFO_SYM ,此名不可更改。
  const 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: "led HAL module",
  author: "gggggg",
  methods: &led_module_methods,
  },
  /* supporting APIs go here */
  };
  以下是具體的文件內容。
  led.h 文件:
  #include
  #include
  #include
  #include
  #include
  #define LED_HARDWARE_MODULE_ID "led"
  struct led_module_t {
  struct hw_module_t common;
  /* support API for LEDServices constructor */
  };
  struct led_control_device_t { struct hw_device_t common; /* supporting control APIs go here */ int (*getcount_led)(struct led_control_device_t *dev); int (*set_on)(struct led_control_device_t *dev); int (*set_off)(struct led_control_device_t *dev); }; struct led_control_context_t { struct led_control_device_t device; }; led.c 文件:
  #define LOG_TAG "LedStub"
  #include
  #include
  #include
  #include
  #include
  #include "../include/led.h"
  static int led_device_close(struct hw_device_t* device)
  {
  struct led_control_context_t* ctx = (struct led_control_context_t*)device;
  if (ctx) {
  free(ctx);
  }
  return 0;
  }
  static int led_getcount(struct led_control_device_t *dev)
  {
  LOGI("led_getcount");
  return 4;
  }
  static int led_set_on(struct led_control_device_t *dev)
  {
  //FIXME: do system call to control gpio led
  LOGI("led_set_on");
  return 0;
  }
  static int led_set_off(struct led_control_device_t *dev)
  {
  //FIXME: do system call to control gpio led
  LOGI("led_set_off");
  return 0;
  }
  static int led_device_open(const struct hw_module_t* module, const char* name,
  struct hw_device_t** device)
  {
  struct led_control_context_t *context;
  LOGD("led_device_open");
  context = (struct led_control_context_t *)malloc(sizeof(*context));
  memset(context, 0, sizeof(*context));
  //HAL must init property
  context->device.common.tag= HARDWARE_DEVICE_TAG;
  context->device.common.version = 0;
  context->device.common.module= module;
  context->device.common.close = led_device_close;
  // 初始化控制 API
  context->device.set_on= led_set_on;
  context->device.set_off= led_set_off;
  context->device.getcount_led = led_getcount;
  *device= (struct hw_device_t *)&(context->device);
  return 0;
  }
  static struct hw_module_methods_t led_module_methods = {
  open: led_device_open
  };
  const 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: "led HAL module", author: "gggggg", methods: &led_module_methods, }, /* supporting APIs go here */ }; Android.mk 文件:
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
  LOCAL_PRELINK_MODULE := false
  LOCAL_SHARED_LIBRARIES := liblog
  LOCAL_SRC_FILES := led.c
  LOCAL_MODULE := led.goldfish
  include $(BUILD_SHARED_LIBRARY)
  三、在 Eclipse 中建一個工程使用我們編寫的 led stub 。
  1. 定義兩個類,源碼如下:
  Myhal.java 文件:
  package com.hello.MyHal;
  import android.app.Activity;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.View;
  import android.widget.Button;
  import com.hello.LedService.LedService;;
  public class Myhal extends Activity implements View.onClickListener {
  static LedService led_srv;
  static Button btn;
  static boolean iflag = false;
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  Log.i("Java App", "OnCreate");
  led_srv = new LedService();
  Log.i("Java App", "Load Java Serivce");
  btn = (Button)this.findViewById(R.id.mybtn);
  btn.setonClickListener(this);
  }
  public void onClick(View v) {
  Log.i("Java App", "btnonClicked");
  String title = new String();
  if (iflag) {
  title = led_srv.set_off();
  btn.setText("Turn On");
  setTitle(title);
  iflag = false;
  } else {
  title = led_srv.set_on();
  btn.setText("Turn Off");
  setTitle(title);
  iflag = true;
  }
  }
  }
  LedService.java 文件:
  package com.hello.LedService;
  import android.util.Log;
  public final class LedService {
  /*
  * load native service.
  */
  static {
  Log.i ( "Java Service" , "Load Native Serivce LIB" );
  System.loadLibrary ( "led_runtime" );
  }
  public LedService() { int icount ; Log.i ( "Java Service" , "do init Native Call" ); _init (); icount = _get_count (); Log.d ( "Java Service" , "Init OK " ); } /* * LED native methods. */ public String set_on() { Log.i ( "com.hello.LedService" , "LED On" ); _set_on (); return "led on" ; } public String set_off() { Log.i ( "com.hello.LedService" , "LED Off" ); _set_off (); return "led off" ; } /* * declare all the native interface. */ private static native boolean _init(); private static native int _set_on(); private static native int _set_off(); private static native int _get_count(); } 其中 LedService 類通過 native 函數使用 led stub 提供的功能。
  2. 接下來我們實現通過 jni 接口實現 native 方法。
  這裏無需使用 javah 生成相應的頭文件。
  com_hello_LedService.cpp 文件:
  #define LOG_TAG "LedService"
  #include "utils/Log.h"
  #include
  #include
  #include
  #include
  #include
  #include "../../led_stub/include/led.h"
  static led_control_device_t *sLedDevice = 0;
  static led_module_t* sLedModule=0;
  static int get_count(void)
  {
  return 0;
  }
  static jint led_setOn(JNIEnv* env, jobject thiz) {
  //if (sLedDevice) {
  LOGI("led_set_on");
  sLedDevice->set_on(sLedDevice);
  //}
  return 0;
  }
  static jint led_setOff(JNIEnv* env, jobject thiz) {
  //if (sLedDevice) {
  LOGI("led_set_off");
  sLedDevice->set_off(sLedDevice);
  //}
  return 0;
  }
  /** helper APIs */
  static inline int led_control_open(const struct hw_module_t* module,
  struct led_control_device_t** device) {
  LOGI("led_control_ope");
  return module->methods->open(module,
  LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
  }
  static jint led_init(JNIEnv *env, jclass clazz)
  {
  led_module_t const * module;
  LOGI("led_init");
  if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
  LOGI("get Module OK");
  sLedModule = (led_module_t *) module;
  if (led_control_open(&module->common, &sLedDevice) != 0) {
  LOGI("led_init error");
  return -1;
  }
  }
  LOGI("led_init success");
  return 0;
  }
  /*
  * Array of methods.
  *
  * Each entry has three fields: the name of the method, the method
  * signature, and a pointer to the native implementation.
  */
  static const JNINativeMethod gMethods[] = { {"_init", "()Z", (void*)led_init}, { "_set_on", "()I", (void*)led_setOn }, { "_set_off", "()I", (void*)led_setOff }, { "_get_count", "()I", (void*)get_count }, }; static int registerMethods(JNIEnv* env) { static const char* const kClassName = "com/hello/LedService/LedService"; jclass clazz; /* look up the class */ clazz = env->FindClass(kClassName); if (clazz == NULL) { LOGE("Can't find class %s ", kClassName); return -1; } /* register all the methods */ if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) { LOGE("Failed registering methods for %s ", kClassName); return -1; } /* fill out the rest of the ID cache */ return 0; } /* * This is called by the VM when the shared library is first loaded. */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; LOGI("JNI_OnLoad"); if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("ERROR: GetEnv failed "); goto fail; } assert(env != NULL); if (registerMethods(env) != 0) { LOGE("ERROR: PlatformLibrary native registration failed "); goto fail; } /* success -- return valid version number */ result = JNI_VERSION_1_4; fail: return result; } Android.mk 文件:
  LOCAL_PATH:= $(call my-dir)
  include $(CLEAR_VARS)
  #LOCAL_MODULE_TAGS := eng
  # This is the target being built.
  LOCAL_MODULE:= libled_runtime
  # All of the source files that we will compile.
  LOCAL_SRC_FILES:=
  com_hello_LedService.cpp
  # All of the shared libraries we link against.
  LOCAL_SHARED_LIBRARIES :=
  libandroid_runtime
  libnativehelper
  libcutils
  libutils
  libhardware
  # No static libraries.
  LOCAL_STATIC_LIBRARIES :=
  # Also need the JNI headers.
  LOCAL_C_INCLUDES +=
  $(JNI_H_INCLUDE)
  # No specia compiler flags.
  LOCAL_CFLAGS +=
  # Don't prelink this library. For more efficient code, you may want
  # to add this library to the prelink map and set this to true.
  LOCAL_PRELINK_MODULE := false
  include $(BUILD_SHARED_LIBRARY)
  將 Led stub , LedService 相關文件放到 development/my_module 分別 make 即可生成相應的 so 文件。使用 adb push 命令安裝到虛擬機上運行即可。
  文件組織機構如下:
  a@ubuntu:~/work/android/source_android/development /my_module$ pwd
  /home/a/work/android/source_android/development/my _module
  a@ubuntu:~/work/android/source_android/development /my_module$ tree hal
  hal
  |-- LedService
  | `-- jni
  | |-- Android.mk
  | `-- com_hello_LedService.cpp
  `-- led_stub
  |-- include
  | `-- led.h
  `-- module
  |-- Android.mk
  `-- led.c
  6 directories, 7 files
  四、編譯運行中遇到的問題及解決方法:
  1.usr/bin/ld: cannot find -lz
  collect2: ld 返回 1
  只是庫命名的問題,簡單的做了個軟鏈接,一切搞定
  ln -svf /lib/libz.so.1 /lib/libz.so
  這個庫文件與軟連接的命名只差了一個 .1
  2.frameworks/base/tools/aidl/AST.cpp:10: error: 'fprintf' was not declared in this scope 的錯誤
  下載 gcc-4.3 和 g++-4.3
  apt-get install gcc-4.3 g++-4.3
  大約十多兆,然後
  進入 /usr/bin
  cd /usr/bin
  建個軟連接
  ln -s gcc-4.3 gcc
  ln -s g++-4.3 g++
  33./bin/bash: flex :找不到命令
  make: *** [out/host/linux-x86/obj/EXECUTABLES/aidl_intermedi ates/aidl_language_l.cpp] 錯誤 127 a@ubuntu:~/work/android/source_android$ sudo apt-get install flex 4.JNI 的 .so 文件放到 /system/lib 下,而 hal moudule 需要放到 /system/lib/hw 下,且命名需符合約定, eg : led.goldfish.so 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章