FireFox OS (B2G) 菜鳥看 code
過去的工作一直都是跟 broadband network 相關,整天看的都是網路封包 (network package),檢查封包從 network device 收進來的 formate 對不對? 封包被改得對不對? Network package 走的路徑對不對? Forwarding 的效率夠不夠好? 封包有沒有掉了(掉包了!!!) ? 掉到哪裡去了? 很少有機會去看比較上層接近 user interface (userspace) 相關的東西,有的話大概也是跟網路管理有關的部分。
轉過來做 Firefox OS,似乎是轉很大,跟之前的領域完全很不一樣,就好像是剛畢業的菜鳥到處找資料,想瞭解什麼是 Firefox OS,架構是長什麼樣子? 還好 Mozilla 有很多前輩寫了很多資料可以參考,很快的,我知道 Firefox OS 是由 Gaia, Gecko 以及Gonk 所組成,也知道
Gaia: User interface of Firefox OS
Gecko: The “application runtime” of Firefox OS
Gecko: The lower-level “operating system” of Firefox OS
但是我沒有找到文章介紹如何將 Gaia、Gecko 跟 Gonk 串起來,也就是我想知道整個程式執行 (code flow) 怎樣從 Gaia → Gecko → Gonk,甚至到 Kernel space?
看來看去,Vibration (叫手機震動),是一個還不錯的例子。
它是很簡單,只是單方向由 Gaia 應用程式 (例如:鬧鈴、簡訊) 呼叫一個 DOM API 去叫手機震動,不需要其他回呼 (call back) 或是等待事件 (event)。
我們就以鬧鈴程式裡的計時器為例,當倒數計時 timeout, 它會呼叫 DOM API,“navigator.vibrate()”, [200,200,200,200,200] 參數是震動的方式。
gaia/apps/clock/js/timer.js
end: function ti_end() {
//TODO: ring too
if (‘vibrate’ in navigator) {
navigator.vibrate([200, 200, 200, 200, 200]);
}
this.cancel();
this.chronoView.parentNode.classList.add(‘ended’);
},
DOM API “navigator.vibrate” 是被定義在 IDL(Interface Description Language) file, “nsIDOMNavigator.idl”
gecko/dom/interfaces/base/nsIDOMNavigator.idl
interface nsIDOMNavigator : nsISupports
{
…
[implicit_jscontext]
void vibrate(in jsval aPattern);
…
}
在程式編譯時,會根據 “nsIDOMNavigator.idl” 產生 header file
“objdir-gecko/dom/interfaces/base/_xpidlgen/nsIDOMNavigator.h”,裡面會定義
NS_IMETHODIMP nsDOMNavigator::Vibrate(const JS::Value & aPattern, JSContext* cx)
注意! 這個地方都有 API 命名的轉換,把第一個小寫字母換成大寫,vibrate → Vibrate。
接著會呼叫
gecko/dom/base/Navigator.cpp
NS_IMETHODIMP
Navigator::Vibrate(const jsval& aPattern, JSContext* cx)
{
…
hal::Vibrate(pattern, domWindow);
return NS_OK;
}
hal:Vibrate() 會呼叫到 Gecko的 hal 層
gecko/hal/Hal.cpp
void
Vibrate(const nsTArray
{
…
hal_impl::Vibrate(pattern, WindowIdentifier());
…
}
然後接著呼叫 hal 層的 implementation。
gecko/hal/gonk/GonkHal.cpp
void
Vibrate(const nsTArray
{
MOZ_ASSERT(NS_IsMainThread());
if (VibratorRunnable::ShuttingDown()) {
return;
}
EnsureVibratorThreadInitialized();
sVibratorRunnable->Vibrate(pattern);
}
創建 vibrator 執行緒
void
EnsureVibratorThreadInitialized()
{
if (sVibratorRunnable) {
return;
}
sVibratorRunnable = new VibratorRunnable();
nsCOMPtr
NS_NewThread(getter_AddRefs(thread), sVibratorRunnable);
}
丟進 vibrator 執行緒
void
VibratorRunnable::Vibrate(const nsTArray
{
MonitorAutoLock lock(mMonitor);
mPattern = pattern;
mIndex = 0;
mMonitor.Notify();
}
在執行緒裡執行 vibrator_on(),讓馬達震動
NS_IMETHODIMP
VibratorRunnable::Run()
{
…
vibrator_on(duration);
…
return NS_OK;
}
這時候 Gecko 部分已經完成,程式要到 Android HAL 層
hardware/libhardware_legacy/vibrator/vibrator.cpp
int vibrator_on(int timeout_ms)
{
/* constant on, up to maximum allowed time */
return sendit(timeout_ms);
}
這裡的 sendit(),會將馬達開啓的時間長度告訴 Kernel driver.
static int sendit(int timeout_ms)
{
int nwr, ret, fd;
char value[20];
…
fd = open(THE_DEVICE, O_RDWR);
if(fd < 0)
return errno;
nwr = sprintf(value, “%d\n”, timeout_ms);
ret = write(fd, value, nwr);
close(fd);
return (ret == nwr) ? 0 : -1;
}
到這裡為止,手機震動的流程已經完成。