深入研究 Firefox OS Emulator for RIL

在先前的文章當中,有跟各位讀者介紹過 Firefox Os EmulatorMarionette 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));
  ...
}

大功告成!是不是沒有想像中的難呢?

深入研究 Firefox OS Emulator for RIL

(圖片來源: Mozilla Tawian 粉絲團)

[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