深入研究 Firefox OS Emulator for RIL
在先前的文章當中,有跟各位讀者介紹過 Firefox Os Emulator 和 Marionette tests,也有進一步討論如何 Hacking Firefox OS emulator。想必各位都很了解 Emulator 和 Marionette 對 RIL (Radio Interface Layer) Developer 有多麼重要吧 (茶~)!
但是 Emulator 畢竟只是一個模擬器,功能不比市面上真正手機中的還要完整,所以很容易遇到一個問題是 Emulator 的支援不夠完整,導致無法撰寫某個 RIL 相關的 Marionette 測試項。別擔心,在以下的文章中,筆者會跟各位介紹如何解決這樣的問題。
以 Bug 860585 為例,筆者想要撰寫 cardLock 相關的 Marionette 測試項,但是卻發現 Emulator 對 cardLock 的支援並不完整 [1]。所以為了要能撰寫 Marionette 測試項,必須要先修正 Emulator 的行為。首先,在 hardware/ril/reference-ril/reference-ril.c 中查看 cardLock 相關的 RIL Request [2] 有沒有被正確的處理:
static void onRequest (int request, void *data, size_t datalen, RIL_Token t) { ... switch (request) { ... case RIL_REQUEST_ENTER_SIM_PIN: case RIL_REQUEST_ENTER_SIM_PUK: case RIL_REQUEST_ENTER_SIM_PIN2: case RIL_REQUEST_ENTER_SIM_PUK2: case RIL_REQUEST_CHANGE_SIM_PIN: case RIL_REQUEST_CHANGE_SIM_PIN2: requestEnterSimPin(data, datalen, t); break; ... } } static void requestEnterSimPin(void* data, size_t datalen, RIL_Token t) { ... if ( datalen == sizeof(char*) ) { asprintf(&cmd, "AT+CPIN=%s", strings[0]); } else if ( datalen == 2*sizeof(char*) ) { asprintf(&cmd, "AT+CPIN=%s,%s", strings[0], strings[1]); } else goto error; ... }
果然筆者發現在 RIL_REQUEST_*_SIM_PIN 中沒有取得正確的參數使用 [3],導致 AT+CPIN [4] 也不能被 qemu 正確的處理。
除此之外,還有另外一個問題:RIL_REQUEST_*_SIM_PIN 應該要回傳還剩下幾次重試,而這部分也並沒有處理完整。這時就必須去詳讀標準文件,在 AT command 的標準文件當中有一個定義 AT+CPINR 的指令用來取得剩下重試次數 [5],我們則需要在 Emulator 中實做這個指令。首先,要在 external/qemu/telephony/android_modem.c 中新增 CPINR 的實作 [6]:
static const struct { const char* cmd; /* command coming from libreference-ril.so, if first character is '!', then the rest is a prefix only */ const char* answer; /* default answer, NULL if needs specific handling or if OK is good enough */ ResponseHandler handler; /* specific handler, ignored if 'answer' is not NULL, NULL if OK is good enough */ } sDefaultResponses[] = { ... { "!+CPINR=", NULL, handleGetRemainingRetries }, /* get remaining PIN retries*/ ... };
+CPINR 前面的 “!" 表示,所有 +CPINR 開頭的指令就會呼叫 handleGetRemainingRetries 來處理。
static const char* handleGetRemainingRetries( const char* cmd, AModem modem ) { ... }
接下來,在 requestEnterSimPin 中利用 CPINR 這個 AT command 取得剩下重試次數 [7],也別忘記該有的錯誤處理也是要有唷!
static void requestEnterSimPin(void* data, size_t datalen, RIL_Token t) { ... asprintf(&cmd, "AT+CPINR=SIM PIN"); err = at_send_command_singleline(cmd, "+CPINR:", &p_response); free(cmd); ... }
最後把 CPINR 的結果,放在 RIL_REQUEST_*_SIM_PIN 的回應中。
static void requestEnterSimPin(void* data, size_t datalen, RIL_Token t) { ... RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, &retries, sizeof(int)); ... }
大功告成!是不是沒有想像中的難呢?
[1] 詳細內容請參考 bug 860682。
[2] 所有的 RIL request 可以參考 https://github.com/mozilla-b2g/platform_hardware_ril/blob/master/include/telephony/ril.h。
[3] 修正的內容請參考 commit bb1878。
[4] 為 AT command 中用來處理 sim lock 的指令,請參考 spec. 3GPP TS 27.007, session 8.3。
[5] 為 AT command 中用來取得剩餘重試次數的指令,請參考 spec. 3GPP TS 27.007, session 8.65。
[6] 詳細的修改內容請參考 commit a68ae4。
[7] 詳細的修改內容請參考 commit da1d3d。