RIL 重構

Android 7.0 使用一組功能重構無線電介面層 (RIL),以改善 RIL 功能。如要實作這些功能 (選用,但建議實作),合作夥伴必須變更程式碼。重構變更具有回溯相容性,因此重構功能的先前實作項目仍可正常運作。

RIL 重構包含下列改善項目:

  • RIL 錯誤代碼除了現有的 GENERIC_FAILURE 代碼外,還可啟用特定錯誤代碼。這有助於提供錯誤原因的更詳細資訊,協助您排除錯誤。
  • RIL 版本管理。提供更準確且更容易設定的版本資訊。
  • 使用喚醒鎖的 RIL 通訊。改善裝置電池效能。

您可以導入上述任一或全部改善措施。詳情請參閱 https://android.googlesource.com/platform/hardware/ril/+/main/include/telephony/ril.h 中 RIL 版本編號的程式碼註解。

實作強化版 RIL 錯誤代碼

幾乎所有 RIL 要求呼叫都能傳回 GENERIC_FAILURE 錯誤代碼,以回應錯誤。這是 OEM 廠商傳回的所有要求回應所產生的問題,如果 RIL 呼叫因不同原因傳回相同的 GENERIC_FAILURE 錯誤代碼,就可能難以從錯誤報告中偵錯。供應商可能需要花費相當長的時間,才能找出程式碼的哪個部分可能傳回 GENERIC_FAILURE 代碼。

在 Android 7.x 以上版本中,OEM 可以傳回與目前歸類為 GENERIC_FAILURE 的每個不同錯誤相關的獨特錯誤代碼值。如果原始設備製造商 (OEM) 不想公開自訂錯誤代碼,可以將錯誤傳回為一組不同的整數 (例如 1 到 x),並將其對應為 OEM_ERROR_1OEM_ERROR_X。供應商應確保每個傳回的遮罩錯誤代碼都會對應至程式碼中的專屬錯誤原因。使用特定錯誤代碼可在原始設備製造商傳回一般錯誤時加快 RIL 偵錯速度,因為 GENERIC_FAILURE 錯誤代碼的確切原因通常需要花費太多時間才能找出 (有時甚至無法找出)。

此外,ril.h 會為 RIL_LastCallFailCauseRIL_DataCallFailCause 的列舉項目新增更多錯誤代碼,以便供應商程式碼避免傳回 CALL_FAIL_ERROR_UNSPECIFIEDPDP_FAIL_ERROR_UNSPECIFIED 等一般錯誤。

驗證強化版 RIL 錯誤代碼

新增錯誤代碼取代 GENERIC_FAILURE 代碼後,請確認 RIL 呼叫傳回的新錯誤代碼,而非 GENERIC_FAILURE

實作強化的 RIL 版本管理

舊版 Android 版本中的 RIL 版本有問題:版本本身不精確,回報 RIL 版本的機制不明確 (導致部分供應商回報錯誤的版本),而且用來估算版本的解決方法容易出錯。

在 Android 7.x 以上版本中,ril.h 會記錄所有 RIL 版本值、說明對應的 RIL 版本,並列出該版本的所有變更。當供應商變更與 RIL 版本相對應的內容時,必須在程式碼中更新版本,並在 RIL_REGISTER 中傳回該版本。

驗證強化版 RIL 版本

請確認 RIL_REGISTER 期間會傳回與 RIL 程式碼相對應的 RIL 版本 (而非 ril.h 中定義的 RIL_VERSION)。

使用喚醒鎖實作 RIL 通訊

在 RIL 通訊中使用定時喚醒鎖定機制的方式不精確,會對電池效能造成負面影響。在 Android 7.x 以上版本中,您可以分類 RIL 要求,並更新程式碼,針對不同要求類型以不同方式處理喚醒鎖定。

分類 RIL 要求

RIL 要求可以是要求或非要求。供應商應進一步將要求的請求歸類為下列任一類別:

  • 同步。要求不需要花費太多時間回應。例如 RIL_REQUEST_GET_SIM_STATUS
  • 非同步。需要相當長時間才能回應的要求。例如:RIL_REQUEST_QUERY_AVAILABLE_NETWORKS

非同步的應答 RIL 要求可能需要相當長的時間。收到供應商程式碼的確認回應後,RIL Java 會釋放喚醒鎖定,這可能會導致應用程式處理器從閒置狀態轉為暫停狀態。供應商程式碼提供回應後,RIL Java (應用程式處理器) 會重新取得喚醒鎖定,處理回應,然後返回閒置狀態。從閒置狀態轉換為暫停狀態,再轉換為閒置狀態,可能會耗用大量電力。

如果回應時間不夠長,在回應期間持續保持喚醒鎖定和閒置狀態,比起在回應到達時釋放喚醒鎖定並喚醒,會更省電。供應商應使用平台專屬的電力測量值,判斷 T 時間的閾值值,當在整個 T 時間內處於閒置狀態時,消耗的電力會大於從閒置狀態轉為暫停狀態,再從暫停狀態轉為閒置狀態時所消耗的電力。T當時間 T 已知時,所需時間超過 T 的 RIL 指令可歸類為非同步,而其餘指令則歸類為同步。

RIL 通訊情境

下圖說明常見的 RIL 通訊情境,並提供修改程式碼的解決方案,以便處理 RIL 要求和非要求的請求。

注意:如要瞭解下列圖表中所用函式的實作細節,請參閱 ril.cpp 中的 acquireWakeLock()decrementWakeLock()clearWakeLock() 方法。

情境:RIL 要求和要求的非同步回應

在這種情況下,如果 RIL 要求的回應需要花費相當長的時間 (例如回應 RIL_REQUEST_GET_AVAILABLE_NETWORKS),則喚醒鎖定會在應用程式處理器端保留很長的時間。數據機問題也可能導致等待時間過長。

圖 1. RIL 要求非同步回應。

解決方案 1:數據機會為 RIL 要求和非同步回應保留喚醒鎖定。

圖 2. 由調製解碼器 (modem) 持有的喚醒鎖定。
  1. RIL 要求會傳送,且數據機會取得喚醒鎖定,以便處理該要求。
  2. MODEM 會傳送確認訊息,導致 Java 端將 wakelock 計數器遞減,並在計數器值為 0 時釋出。

    注意:要求-確認序列的喚醒鎖定逾時時間會比目前使用的逾時時間短,因為系統應該會很快收到確認。

  3. 處理要求後,數據機會傳送中斷至供應商程式碼,以便取得喚醒鎖定,並傳送回應至 ril.cpp,而 ril.cpp 會取得喚醒鎖定,並傳送回應至 Java 端。
  4. 回應傳送至 Java 端時,系統會取得喚醒鎖,並將回應傳回給呼叫端。
  5. 所有模組處理回應後,系統會透過 Socket 將確認訊息傳回至 ril.cpp,接著釋出在步驟 3 取得的喚醒鎖。

解決方案 2:數據機不會保留喚醒鎖定,且回應速度快速 (同步 RIL 要求和回應)。同步和非同步行為會為特定 RIL 指令硬式編碼,並根據個別呼叫決定。

圖 3. 數據機未保留喚醒鎖定。
  1. RIL 要求會透過在 Java 端呼叫 acquireWakeLock() 傳送。
  2. 供應商程式碼不需要取得喚醒鎖定,而且可以快速處理要求並回應。
  3. 當 Java 端收到回應時,系統會呼叫 decrementWakeLock(),該函式會減少喚醒鎖計數器,並在計數器值為 0 時釋放喚醒鎖。

情境:RIL 未經要求的回應

在這種情況下,RIL 非預期回應會在其中包含喚醒鎖類型標記,指出是否需要為供應商回應取得喚醒鎖。如果設定了這個標記,系統會設定計時喚醒鎖定,並透過 Socket 將回應傳送至 Java 端。計時器到期後,系統就會釋放喚醒鎖定。針對不同的 RIL 非預期回應,時間定時喚醒可能過長或過短。

圖 4. RIL 非預期回應。

解決方法:請從 Java 程式碼將確認訊息傳送至原生端 (ril.cpp),而不是在傳送非預期回應時,在原生端保持計時喚醒鎖定。

圖 5. 使用 ack 而非定時喚醒鎖定。

驗證重新設計的喚醒鎖定

確認 RIL 呼叫是否已識別為同步或非同步。由於電池耗電量可能會依硬體/平台而異,供應商應進行一些內部測試,找出是否可使用新的喚醒鎖定語義來進行非同步呼叫,進而節省電池電量。