綜觀市場,若能有效的整合開放式作業系統平台,使多媒體應用,可不受限單一作業系統內部執行環境的規範,產品發展將朝向更多元的行動裝置,勢必可大幅提升多媒體應用軟體研發意願,屆時將可有效的為兩造雙方創造市場雙贏。因此筆者提出了一個Android與其它Linux-based平台做結合的方式,讓Linux-based平台的使用者除了原本的軟體外還可以使用Android的應用程式。
技術簡介
一般Linux-based的系統通常在應用軟體部分都是可以互相流通的,但Android的應用軟體卻不行,是因為Google在考量資源有限的情況下,自行發展出與一般Linux版本不同的許多套件,首先,它不使用glibc為它的C函式庫,而是採用Google自行開發的Bionic Libc。Bionic Libc大小約為glibc的一半,並提供Android所需要的重要函數,但不完全支持posix標準,且在加載動態庫時不是使用常用的linker等。
再者,在GUI方面,不使用在Linux平台上通用的X window system,而是自行開發一個完整的圖形環境。並為了使用Apache Harmony所提供大量的Java函式庫,採用Java為開發Android 應用程式的語言,而且自行開發Java Virtual Machine - Dalvik VM,設計上使用較少的記憶體資源等。在Linux kernel也新增了一些Driver提供上層使用,譬如Binder Driver,提供給上述圖形環境有效率及安全的IPC, 另外還有Log Driver,Low Memory Killer等。
因此,若想要將Android的應用程式在其它Linux上運行,除了會遇到Java Virtual Machine的不同問題,還會有應用程式跟Android整個環境的相依性的問題。當筆者深入了解Android整個啟動流程,發現Android的應用程式倚賴著Android Framework: Activtiy Manager、Package Manager、Window Manager、Resource Manager等服務,所以理論上在相似的平台上,只要這些服務正常運作,Android的應用程式就可以執行。
故,筆者採行的方法,主要就是讓Android的服務在其他Linux版本上運行,並為了與原本存在其他Linux版本上的視窗環境結合,改變其輸入裝置的事件處理及畫面的輸出的方式。
實作上,利用一支Linux程式去啟動所有Android的服務,並撰寫一支X client的程式輸出Android的畫面,以及接收X server的input events。整個Android作業系統像是一支程式,而非每支Android行程都獨立出一個視窗,但由於Android本身每支程式都是獨占畫面,因此這樣的結合讓畫面看起來仍是流暢的,進而提供了一個簡單的方式讓Android可以在其它Linux版本運行。
在此筆者採Moblin為實作的平台,Moblin使用一般Linux平台上glibc及X window system,並可使用標準的Linux kernel。
整合步驟
筆者提出的整合架構如圖一所示。在一個硬體平台上,將Android及Moblin用user space及kernel space的角度分開來看,在kernel space,Moblin與Android共用Linux kernel,而在user space部分,Moblin applications及Android applications使用各自獨立的GUI環境及libraries,而銜接兩者最重要的地方就在pipes、Shared Memory及X client,這讓Android可以與X window system共存。
以下筆者分別就 Linux Kernel、Root Filesystem以及Android Services這三部份來討論詳細的整合流程。
《圖一 Android over Moblin架構圖》 |
Linux Kernel —
如上所述,Android上層應用程式依賴Android對標準kernel外加的驅動程式之支援,而Moblin或大部份其它Linux平台雖在kernel也有些修改,但主要都是針對kernel內部效能的調教,所以不會對user space的應用程式構成相容的問題,因此筆者選用Android發佈的kernel當整合後平台的kernel。另外,kernel的選項則要考慮到兩個系統應用程式的需求,如在此筆者需打開KMS以支援Moblin,同時也需開啟對Android驅動程式的支援。
Root Filesystem —
完成kernel的設定後,就可以開始user space的整合。一般Linux版本的root filesystem主要目錄結構如圖二所示。在筆者要整合的兩個平台中, Moblin使用與表一同樣的目錄結構,而Android則不盡然相同,其保有/sbin、/proc、/sys以及/dev,而把所有工具、應用程式和函式庫等都放到/system下,因此/system在Android中佔有重要地位,也因為函式庫路徑不同,Android鏈結的函式庫得以跟Moblin分開,避開同樣是libc函式庫,實際上卻使用不同函式庫的問題,讓這部分不需額外處理。
由於/proc、/sys以及/dev都是kernel產生與系統相關的目錄,因此不需另外處理;而/sbin(內只含adbd)裡的檔案則需保留進Moblin的/sbin裡,/system則需整個複製到Moblin的root filesystem下。
《圖二 圖二:Linux Root Filesystem 目錄結構》 |
Android Services
如簡介中提到的,筆者須先讓Android 系統服務在其他Linux平台中執行起來,如此Android的應用程式就可以在此平台上執行。由於Android開機的第一支程式init替Android的應用程式準備好執行環境(Runtime),除了叫起一些需要的Linux daemons,如adbd、vold、mediaserver及installd外,並啟動Android中很重要的程式—Zygote,Zygote控管Android的Java行程,依據要求產生Dalvik VM,並在init裡,會先生出一個Dalvik VM跑system_server,而system_server開啟了所有Android重要的服務。
另外所有使用者界面上主要的應用程式,如calendar、media及alarmclock等等,也都是執行在Zygote生出的Dalvik VM內。因此筆者修改init這支程式,使其在Moblin上將Android的執行環境準備好。
除此之外,原本Android單獨存在時,Android的畫面是將要顯示的資料直接寫到顯示卡的framebuffer去顯示,與使用者互動的輸入是直接收kernel送出的events,而當運行在Moblin上時,由於Moblin有另外一套GUI程式也會存取硬體資源,因此Android直接存取硬體資源會與Moblin不相容,所以Android必須與Moblin上其他GUI程式一樣使用X window system,顯示及接收使用者的輸入都要透過X server。而顯示及接受輸入的程式碼分別寫在EGLDisplaySurface.cpp及EventHub.cpp裡,一種作法是將裡面的函式更改成使用X library與X server做溝通,但是,像前面所講的,由於Android使用的libc函式庫非一般在Linux上使用的glibc,在linker方面也非一般Linux使用的ld.so,要將龐大的X library重新用Android的toolchain重新編譯是件大工程,因此筆者採一個較為簡潔的方法,另外寫一支X client,並利用Shared Memory及pipes去跟Android做連接(
如前述圖一),而非將Android內部的程式改成使用X library。
另外,針對設備的新增及移除,Linux kernel會發送uevent來告知,專職的程式如udev就可以利用uevent夾帶的訊息來判斷設備的類型跟狀態,並做相對應的動作,如在/dev下新增或移除一個device node等。Moblin上使用常用的udev來做設備的管理,而Android則是設計了一個佔據較少資源的設備管理員。然而,同一平台上不需要兩個設備管理員,因此需移除Android的設備管理員,並將Android對其特殊設備的設定加到udev的rules裡,由udev全權管理。
照著以上的更改完後,筆者就可以在Moblin上將Android像啟動一般UI應用程式般啟動。接下來就談到設備共用的部份 :
設備共用
在設備方面,筆者以一些設備為例,如Camera、GPS及SD Card/USB Storage等來說明同時使用時的狀況。在大部分的實作上跟將Android移植到x86架構上類似,但由於是與Moblin系統共存,很多功能可能與Moblin原本擁有的應用程式相同,所以除了考慮系統上可能的衝突外,開啟程式時也要注意到是否硬體已經在使用中。
Camera & GPS
Android 對於一些特殊的硬體裝置大多使用定義好的HAL(hardware abstract layer) 作為中介層,因此基本上只需要實作與硬體相關的程式碼將HAL與底層 driver 提供的介面銜接起來即可,至於HAL如何與上層的service做溝通,Android 本身已經具有相當完整的實作,故不需要另外處理。像Camera以及GPS即屬於這類裝置, 當共用時,只需考慮到是否有其他程式已經在使用這項設備,實作上則與移植Android到x86架構相同,如下:
Camera
Android定義好HAL介面, 但是並沒有真正實作從鏡頭擷取影像的功能。因此筆者主要的工作便是利用V4L(Video for Linux)來擷取影像。Video4linux 是Linux中對底層影像擷取設備的驅動,必須先行編譯kernel使其支援該功能。而影像擷取的大致流程如下: 打開影像擷取設備(/dev/video0)、 讀取設備訊息、 初始化設備、影像擷取、影像處理或轉換、 關閉設備 。
GPS
一般藍芽的 GPS 裝置連接後,在 Linux 內通常會以 rfcomm(radio frequency communication) 的介面出現,該介面的操作就相當於一般的 RS-232 serial port 相同。將其開啟後,GPS 裝置就會傳送一系列 NMEA 編碼的資料過來,於是筆者便能使用此資料來讓 HAL 能讀取並轉送 GPS 座標給上層的 location service。
在實作上,主要要實作的就是一個 GPS library,而 Android 在原始的程式碼內就有一個可以參考的程式碼
($ANROID_SRC_ROOT/hardware/libhardware_legacy/gps/gps_qemu.c),所以只需對其稍加修改,自 rfcomm 介面讀取目前的 GPS 資料,基本上就能順利的將 Android 與 GPS 裝置銜接起來。
《圖三 HAL》 |
SD Card/USB Storage
在storage方面,Android 版本1.5後採用vold來掛載小型周邊。vold裡沒有針對USB storage所屬的block device做處理,因此如想要掛載USB storage,需自行增加處理block device。跟前面所講到的裝置管理員一樣,vold是利用uevent來判斷硬體狀態,除對裝置新增移除的動作外,在同時也會依狀態決定是否要通知media scanner針對儲存裝置裡的內容做掃描,以供預覽。由於有些功能與udev重複,因此在處理函式裡要考慮到會衝突的部份,譬如說在收到uevent時,不對裝置做新增移除的動作,讓udev去完成這部份。
相關研究
前面所述是直接修改Android與硬體銜接的部份,去達成讓Android與其它Linux-based系統並行,以下提出其他方式:
Virtual Machine
透過Virtual Machine,如使用Android SDK提供的模擬器Qemu也可以讓Android與其它系統共存上,但這種作法由於Android不是跑在原生硬體(Native Hardware)上,效能上會比較差。
Android execution environment by Canonical
目前讓Android不另外透過模擬器,還有Ubuntu的商業贊助商Canonical的作法。
Canonical在建造一個Android execution environment,雖未公佈其完整的實作方法與產品,但在網路上已有部分實作結果及進展。其方法使用了glibc而非bionic,忽略掉Android為IPC所做的Binder driver,並打算刪掉一些特殊元件,因此目前還有許多限制及功能不能使用,但設計上讓Android的應用程式能透過Android execution environment 獨立執行,並不像筆者是將整個Android當成一個應用程式,因此更無縫的結合,也增加了實作上的困難及複雜度。
結語
以上建議,主要是以不影響Android與Moblin的大部分元件、架構下,讓Android可同時在其它Linux平台上直接運行,並分析整合每一個流程步驟,剖析其與一般傳統的Linux平台的異同處,藉由討論,促成對該議題有興趣的研發技術者,可以從中再去延伸、改善,如加入3D圖形加速等。