Jollen: Android 的 HAL 技術

Android 的 HAL 技術, #1: 簡介與發展現況

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 的開發能在不考量驅動程式的前提下進行發展。 html

在 Android 原始碼裡,HAL 主要的實做儲存於如下目錄: android

1. libhardware_legacy/ - 過去的實做、採取程式庫模組的觀念進行
2. libhardware/ - 新版的實做、調整為 HAL stub 的觀念
3. ril/ - Radio Interface Layer app

在 HAL 的架構實做成熟前(即圖1的規劃),我們先就目前 HAL 現況作一個簡單的分析。另外,目前 Android 的 HAL 實做,仍舊散佈在不一樣的地方,例如 Camera、WiFi 等,所以上述的目錄並不包含全部的 HAL 程式碼。 框架

HAL 的過去 ide


圖2:Android HAL / libhardware_legacy

過去的 libhardware_legacy 做法,比較是傳統的「module」方式,也就是將 *.so 檔案當作「shared library」來使用,在 runtime(JNI 部份)以 direct function call 使用 HAL module。透過直接函數呼叫的方式,來操做驅動程式。 google

當然,應用程式也能夠不須要透過 JNI 的方式進行,直接以載入 *.so 檔(dlopen)的作法呼叫 *.so 裡的符號(symbol)也是一種方式。 atom

HAL 的現實狀況 spa


圖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,就能夠取得操做函數。 .net

HAL 的未來發展? 代理

新的 HAL 作法,傾向全面採用 JNI 的方式進行。也就是,在 Android 的架構中,修改 Android runtime 實做(即「Core Library」),在取得 HAL 模組的 operations 後再作 callback 操做。將 HAL 模組徹底放在 HAL 裡面。


Android 的 HAL 技術, #2: 採用Service架構方式

在上一篇日記裡,我們介紹了Android HAL的大概念。接下來,將慢慢地進行細部的分析與研究。首先,我們先針對HAL的現況先作討論,後續再針對HAL的設計提出觀念。

圖1:採用Service架構與不採用Service架構

如圖1,應用程式存取驅動程式的過程,可區分為如下二種:

  • 採用Service架構方式
  • 不採用Service架構方式

採用Service架構方式是比較標準的作法,即圖上藍色線的部份;紅色線的部份為非Service架構式的作法。先前,在「Android 驅動開發關鍵技術:HAL及移植要領」演講中最後所提出的一個LED範例,便是一種非架構式的作法,簡報上有一段範例程式碼,歡迎下載參考。

Android HAL Introduction: libhardware and its legacy

採取Service架構的方式,是建議的作法,當然因為這是標準架構,也應該採用。

Service在Android框架裡的角色是「存取底層硬體」,往上層的話,能夠和應用程式溝通。所以,採用標準的Service作法,好處是在 資料存取(data communication)的處理較為系統化。這方面,Android提供了標準的處理架構,後續再進行討論。

圖上的「core libraries」便是Service程式碼的實做,也就是,Android應用程式透過JNI(Dalvik)來到Service這一層,再透過Service載入*.so檔;而非標準作法則是讓應用程式直接透過JNI來載入*.so檔。

不使用Service架構

紅色的過程,因為不使用Service架構,所以「框架整合」的工做量比較小,甚致大部份的實做都不須要改動框架自己。這樣的作法,就有點像是「跳過framework」的方式;相對的,此時應用程式開發者須要考慮的設計議題就比較多。

例如,當遇到block operation時,是否須要獨立的Java thread來處理?


Android 的 HAL 技術, #3: 小探Android Service與Native Service

前二篇教學提到「採用Service架構整合HAL的作法」。這裡再針對HAL如何採用Service架構與框架整合作一個概念的介紹。

Android的Service分為二種:Android Service與Native Service。

Android Service

Android Service又稱為Java Service,是實做在框架層(framework)裡的「Server」。這裡所講的「Service」是System Service,又稱為Server,與應用程式設計上所討論的Service(android.app.Service)不一樣。Android Service以Java撰寫。

Native Service

Native Service則是實做在Runtime層裡的Server。架構設計上,我們有二個選擇,一個是實做Android Service、再透過JNI與HAL stub溝通;另外一個選擇是,跳過Android Service,讓Application(Manager API)直接與Native Service溝通。

未來的Android發展趨勢,應會以第二種作法為主,即Manager API直接與Native Service溝通,以達到更好的效能表現。


Android 的 HAL 技術, #4: Android Service 與 HAL Stub


目前為止,我們提了「HAL Stub」的觀念,瞭解到 stub 是一種代理人的觀念,架構設計上,採取「provide operations」與「callback」機制,而不是採用「module」即 library 的 direct function call 作法。

接著,又提到「採取Service架構的方式」。在講解HAL stub的實做細節前,須要大略瞭解一下Android Service與HAL stub的關係;因為,架構設計上是「透過Android Service取得HAL stub提供的operations」。

取得HAL stub operations的程式碼實際上是實做在Native Service裡,相關的實做細節留待後續再作說明。

Android Service與HAL stub的關係

應用程式(Application)使用了Manager API,Manager API經由Android Service來到Native Service層,最後Native Service層再引用(invoke)HAL stub。這個過程,總共經過瞭如下3個介面,以下圖。



1. 這是一個 remote interface,應用程式與Android Service在不一樣的 process 上執行。

2. 這是 Java native interface,實做上透過 JNI table 來對應 native method 與 native function。

3. 理論上,這是 HAL 層,實做上則是使用 HAL API 先取得 stub operations,再由 native service callback stub。




Android 的 HAL 技術, #5: 繼承 HAL 的 struct hw_module_t

撰寫 HAL stub 除了要具備系統程式(systems software)的觀念外(這是基礎),「思考方式的改變」也是重要的一堂課。

思考方式哪裡不一樣?

實做 HAL stub 的首要工做是「繼承 struct hw_module_t 抽象型別」。Class(類別)屬於一種抽象型號(ADT)。

首先,引入最重要的標頭檔(header file):

#include <hardware/hardware.h>

接著,再定義一個「MODULE ID」。這個 mdoule ID 將會被 HAL 層用來尋找 HAL stub。我們舉最簡單的裝置類型「LED」來作為範例:

#define LED_HARDWARE_MODULE_ID "led"
繼承 HAL 的 struct hw_module_t 抽象型別(即 base class 的概念),並取名為 struct led_module_t(即 derived class):
struct led_module_t {
   struct hw_module_t common;
};

以資料結構的角度來看,這裡的作法只是宣告了一個抽象資料型別(Abstract Data Type),以提高程式碼的結構化特性。可是,這裡須要以架構的角度來解釋,「Android HAL規定不要直接使用struct hw_module_t」,原文的意思是要我們作類別繼承。

實做繼承

在C語言裡實做繼承的方式,大體以下:

1. 宣告一個 data structure 將原始的基本結構包裝起來
2. 將原始的基本結構放在第一個 field

所以,能夠思考以下:

struct led_module_t {
   struct hw_module_t common;
    
   /* Place attributes here. */
 
   /* Place methods here. */
};

惟一,這裡的實做在OO特性上,缺少像是public與private的封裝性。但這裡的重心是,以OO的方式思考,會如何改變過去的 C 程式寫做習慣?最明顯的地方是,程式碼的寫做風格有了很大的改變



Android 的 HAL 技術, #6: 小結 HAL stub 實做步驟


在討論了很多基本概念後,在這裡小結一下 HAL stub 的實做步驟。HAL stub 的起頭是「繼承 HAL 的 struct hw_module_t」,這是 HAL stub 的設計理念,除了讓架構的條理分明外,也容易作後續擴充與維護。如下改用實用上的習慣用語,小結一下 HAL stub 實做步驟,並提供一段例。

HAL Stub 實做步驟(Implementation)

1. 設計自已的wrapper data structure

* 編寫led.h
* 定義 struct led_module_t
* 框架提供的 struct hw_module_t 必須放在第一個 field、並取名為 common
* 請參考 hardware/hardware.h

2. led_module_t的意義

宣告初始化時期(new object)的 supporting API、在 constructor 裡會使用到。

3. 定義 led_control_device_t

宣告控制時期的 supporting API、在 Manager API 裡會使用到。設計上的細節在後續文章再作整理。

4. 每個 HAL stub 都要宣告 module ID

5. 宣告 Stub operations 並實做 callback functions

Stub 的 operations 結構符號名稱須取名為 HAL_MODULE_INFO_SYM、此符號名不可更改。

範例:led.h

#include <hardware/hardware.h>
   
#include <fcntl.h>
#include <errno.h>
   
#include <cutils/log.h>
#include <cutils/atomic.h>
   
/*****************************************************************************/
   
struct led_module_t {
   struct hw_module_t common;
};
   
struct led_control_device_t {
   struct hw_device_t common;
   /* supporting control APIs go here */
   int (*set_on)(struct led_control_device_t *dev, int32_t led);
   int (*set_off)(struct led_control_device_t *dev, int32_t led);
};
   
/*****************************************************************************/
   
#define LED_HARDWARE_MODULE_ID "led"
實做上的細節在這裡再也不多作說明,關於設計上的細節將另行整理。


Android 的 HAL 技術, #7: 取得 Proxy Object

延續上一則日記的介紹,在完成HAL Stub的實做後,緊接著的工做就是撰寫Native Service。

談了許多「Android Service」以及「HAL Stub」,這裡再補充一點。Android做業系統啟動時,會執行一個process稱為servicemanager。Servicemanager process負責管理、提供並保存「Android Service」。Android Service為Java層,所以接下來會透過JNI來呼叫C/C++層的Native Service。

廣義來說,Native Service也提供Runtime的功能,即Core Library層。Runtime的重要工做之一為「取得HAL Stub所提供的API」,所以這是撰寫完整Native Service的前哨站。

什麼是 Proxy Object?

Native Service呼叫HAL的hw_get_module() API取得stub物件,即HAL_MODULE_INFO_SYM。

HAL會去尋找HAL Stub檔案,HAL Stub是以*.so檔的形式存在,並佈署於/system/lib/hw目錄下。HAL會根據module ID以及」ro.product.board」去尋找相對應的*.so檔,以我們的LED範例來說,HAL會回傳回led_module_t結構的物件 (an instance of led_module_t class)給Native Service。

我們把HAL回傳給Native Service的資料稱為「Stub Object」或是「Proxy Object」,即先前所說起的「代理人」觀念。Native Service透過代理人與 Linux 驅動程式溝通。這個過程的觀念如圖1所示。



圖1:取得Proxy
 Object hw_get_module(),這是HAL所提供的API,也是實做HAL Stub最重要的一個API,不過另人驚奇的地方是,這也是目前AOSP裡HAL層所提供的惟一一個API。

Android 的 HAL 技術, #8: 實做 HAL Stub

承日記「Android 的 HAL 技術, #6: 小結 HAL stub 實做步驟」與「Android 的 HAL 技術, #7: 取得 Proxy Object」。在瞭解基本的觀念,以及架構上的設計後,接著就能夠開始實做 HAL Stub 了。如下是 LED Stub 的實做範例,將程式碼儲存為 led.c:

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: "Simple LED module",
        author: "The Mokoid Open Source Project",
        methods: &led_module_methods,
    }
 
    /* supporting APIs go here */
};

這段程式碼實做了圖1的設計, led_module_t 是 Stub 的主體結構(或稱為主類別),其符號名稱須取名為 HAL_MODULE_INFO_SYM,不可更改。任何的 Stub 主類別名稱都須命名為 HAL_MODULE_INFO_SYM。



圖1:LED Stub的設計(OOD)

幾個重要的欄位以下:

  • tag:須指定為 HARDWARE_MODULE_TAG
  • id:指定為 HAL Stub 的 module ID,我們的範例為」LED」
  • methods:struct hw_module_methods_t,為 HAL 所定義的「method」
  • struct hw_module_methods_t 是由 HAL 定義的標準介面,目前的 AOSP(Android Open Source Project)實做裡包含一個」open」介面

"Open" 是一個介面(interface),這意味著 HAL Stub 必須實做此介面,這個部份更是 HAL 的重點。

Jollen 的 Android HAL 技術解析, #9: 理想中的完整架構


一年多前,「Android 的 HAL 技術」系列日記闡釋了 Android HAL 的觀念與基本設計方法;在小弟的「Android HAL & Framework: 軟硬整合實做訓練 」訓練規劃中,也對 Android Service 與 HAL 作了完整的介紹。

現在,再度回到 Android HAL 的主題;主要是 Android 2.3 在部份硬體單元(Component)作了一些架構調整。隨著 AOSP 程式碼的大幅進步,硬體廠須要加強對 Android HAL 的技術掌握度,這是十萬火急的工做了。因為,硬體廠惟有具備發展高品質軟硬整合程式碼的能力,才能在 Android 產品開發工做上具備競爭力。

「Native 化」

過去在「Android 的 HAL 技術, #3: 小探Android Service與Native Service」 裡說起 Android 應用程式存取硬體的作法,大體分為二個路徑:Android Service 與 Native Service。在「Android HAL & Framework: 軟硬整合實做訓練 」裡,分別以「紅色路徑」與「綠色路徑」來表示。

在日記「Android 2.3 的更新:SensorService 的 Native 化」裡提到的「Native 化」會成為日後 Android 做業系統發展的趨勢之一。「也就是紅色路徑慢慢往綠色路徑移動」。

理想中的 Android HAL 架構

要了解這項發展趨勢,能夠從理想中的 Android HAL 架構說起,先在腦海裡創建大方向圖(Big Picture)。根據 Android 各版本的發展,能夠推測出 Android HAL 將朝向下圖的架構來發展。

這個架構不是憑空想像,事實上也不是 Android 做業系統的創舉。

研究做業系統的朋友能夠知道,Hardware Abstraction Layer (HAL) 的觀念已經行之多年了,在許多重量級的 OS 裡都能看到。為什麼在過去的 Android OS 裡,HAL 的架構或程式碼如此陽春?簡單講,「就是還沒有發展成熟。」



圖一:理想且完整的 Android HAL 架構

根據我們的研究,理想中且完善的 Android HAL 架構包含四個部份:

  • Service Layer 屬於 Application Framework 層,提供 Application 存取硬體的「服務」。理論上,這些服務是以 instance 的形式存在。
  • Hardware Abstraction Layer 介於 Application Framework 與 Driver Framework 之間,以抽象形式存取硬體。理論上,HAL 層的存在形式有三種 - library (module) / server / communication bus。
  • Native Servier 屬於整個 Android 做業系統最底層。理論上,它應該在 HAL 層之下,也是 HAL 不會是我們想像中「隔絕 kernel 的最下層」,存在形式為 daemon。
  • Driver Framework 屬於 kernel-space 層,也就是包含在 Linux kernel 裡,「很是 long-term 來看」也能夠是其它 OS。理論上,Driver Framework 是以 instance 的形式存在。

Service Layer 實做在 Application Framework 層,為了提供更上層(Application)存取硬體而設計,所以,在有些研究上,也稱之為「Application Service」;等同於過去我們所講的 Android Service。Application Service 最多就是提供 API 而已。

這個架構並非一種創新,因為在許多重量級的做業系統裡都能看到這樣的設計,例如:Mac OS X。統一架構,讓硬體存取一致化,實做整齊劃一。不過,以上的觀點都只是理論,目的是推測 Android 在這個部份的發展輪廓。

實務上,看到的程式碼與理論研究總是有點落差。現行的 AOSP 程式碼實做,還是與我們所討論的架構不一樣;至於未來如何發展,只能密切與 AOSP 同步。螢幕上的程式碼,背後總是有許多思維邏輯,有些能從程式碼推敲一二,更多則是沒有文獻記載,技術研究的樂趣在於發覺這些內涵。

相關文章
相關標籤/搜索