Android 的 HAL 技術, #1: 簡介與發展現況
jollen 發表於 October 8, 2009 2:05 AM
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 的未來發展?
新的 HAL 做法,傾向全面採用 JNI 的方式進行。也就是,在 Android 的架構中,修改 Android runtime 實作(即「Core Library」),在取得 HAL 模組的 operations 後再做 callback 操作。將 HAL 模組完全放在 HAL 裡面。
--jollen
Android 的 HAL 技術, #2: 採用Service架構方式
jollen 發表於 October 14, 2009 6:17 PM
在上一篇日記裡,我們介紹了Android HAL的大概念。接下來,將慢慢地進行細部的分析與研究。首先,我們先針對HAL的現況先做討論,後續再針對HAL的設計提出觀念。
圖1:採用Service架構與不採用Service架構
如圖1,應用程式存取驅動程式的過程,可區分為以下二種:
- 採用Service架構方式
- 不採用Service架構方式
採用Service架構方式是比較標準的做法,即圖上藍色線的部份;紅色線的部份為非Service架構式的做法。先前,在「Android 驅動開發關鍵技術:HAL及移植要領」演講中最後所提出的一個LED範例,即是一種非架構式的做法,簡報上有一段範例程式碼,歡迎下載參考。
採取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來處理?
--jollen
Android 的 HAL 技術, #3: 小探Android Service與Native Service
jollen 發表於 November 27, 2009 1:59 AM
前二篇教學提到「採用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
jollen 發表於 November 28, 2009 9:56 PM
目 前為止,我們提了「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
jollen 發表於 December 1, 2009 10:09 PM
撰寫 HAL stub 除了要具備系統程式(systems software)的觀念外(這是基礎),「思考方式的改變」也是重要的一堂課。
思考方式哪裡不同?
實作 HAL stub 的首要工作是「繼承 struct hw_module_t 抽象型別」。Class(類別)屬於一種抽象型號(ADT)。
首先,引入最重要的標頭檔(header file):
#include
接著,再定義一個「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 程式寫作習慣?最明顯的地方是,程式碼的寫作風格有了很大的改變。
--jollen
Android 的 HAL 技術, #6: 小結 HAL stub 實作步驟
jollen 發表於 December 4, 2009 10:49 PM
在 討論了不少基本概念後,在這裡小結一下 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 #include #include #include #include /*****************************************************************************/ 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"實作上的細節在這裡不再多做說明,關於設計上的細節將另行整理。
--jollen
Android 的 HAL 技術, #7: 取得 Proxy Object
jollen 發表於 December 26, 2009 11:36 PM
延續上一則日記的介紹,在完成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。
--jollen
Android 的 HAL 技術, #8: 實作 HAL Stub
jollen 發表於 January 2, 2010 11:31 PM
承日記「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。
幾個重要的欄位如下:
- 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