Rifacimento del codice RIL

Android 7.0 ha eseguito il refactoring del Radio Interface Layer (RIL) utilizzando un insieme di funzionalità per migliorare la funzionalità RIL. Per implementare queste funzionalità, che sono facoltative ma consigliate, è necessario apportare modifiche al codice del partner. Le modifiche al refactoring sono compatibili con le versioni precedenti, quindi le implementazioni precedenti delle funzionalità sottoposte a refactoring continuano a funzionare.

Il refactoring di RIL include i seguenti miglioramenti:

  • Codici di errore RIL. Attiva codici di errore specifici oltre al codice GENERIC_FAILURE esistente. Ciò aiuta a risolvere gli errori fornendo informazioni più specifiche sulla causa degli errori.
  • Controllo delle versioni RIL. Fornisce informazioni sulla versione più accurate e più facili da configurare.
  • Comunicazione RIL tramite wakelock. Migliora le prestazioni della batteria del dispositivo.

Puoi implementare uno o tutti i miglioramenti sopra indicati. Per maggiori dettagli, consulta i commenti al codice sul controllo delle versioni RIL in https://android.googlesource.com/platform/hardware/ril/+/android16-release/include/telephony/ril.h.

Implementare codici di errore RIL avanzati

Quasi tutte le chiamate di richiesta RIL possono restituire il codice di errore GENERIC_FAILURE in risposta a un errore. Si tratta di un problema con tutte le risposte sollecitate restituite dagli OEM, che può rendere difficile il debug di un problema dal report sui bug se lo stesso codice di errore GENERIC_FAILURE viene restituito dalle chiamate RIL per motivi diversi. Potrebbe essere necessario molto tempo prima che i fornitori identifichino la parte del codice che potrebbe aver restituito un codice GENERIC_FAILURE.

In Android 7.x e versioni successive, gli OEM possono restituire un valore di codice di errore distinto associato a ogni errore diverso attualmente classificato come GENERIC_FAILURE. Gli OEM che non vogliono rivelare pubblicamente i propri codici di errore personalizzati possono restituire gli errori come un insieme distinto di numeri interi (ad esempio da 1 a x) mappati come OEM_ERROR_1 a OEM_ERROR_X. I fornitori devono assicurarsi che ogni codice di errore mascherato restituito corrisponda a un motivo di errore univoco nel codice. L'utilizzo di codici di errore specifici può accelerare il debug di RIL ogni volta che vengono restituiti errori generici dall'OEM, in quanto spesso è necessario troppo tempo per identificare la causa esatta di un codice di errore GENERIC_FAILURE (e a volte è impossibile scoprirlo).

Inoltre, ril.h aggiunge altri codici di errore per gli enum RIL_LastCallFailCause e RIL_DataCallFailCause, in modo che il codice del fornitore possa evitare di restituire errori generici come CALL_FAIL_ERROR_UNSPECIFIED e PDP_FAIL_ERROR_UNSPECIFIED.

Convalida dei codici di errore RIL avanzati

Dopo aver aggiunto nuovi codici di errore per sostituire il codice GENERIC_FAILURE, verifica che i nuovi codici di errore vengano restituiti dalla chiamata RIL anziché da GENERIC_FAILURE.

Implementare il controllo delle versioni RIL avanzato

Il controllo delle versioni RIL nelle versioni precedenti di Android era problematico: la versione stessa era imprecisa, il meccanismo per segnalare una versione RIL non era chiaro (alcuni fornitori segnalavano una versione errata) e la soluzione alternativa per stimare la versione era soggetta a imprecisioni.

In Android 7.x e versioni successive, ril.h documenta tutti i valori della versione RIL, descrive la versione RIL corrispondente ed elenca tutte le modifiche per quella versione. Quando apportano modifiche corrispondenti a una versione RIL, i fornitori devono aggiornare la propria versione nel codice e restituirla in RIL_REGISTER.

Convalida il controllo delle versioni RIL avanzato

Verifica che la versione RIL corrispondente al codice RIL venga restituita durante RIL_REGISTER (anziché RIL_VERSION definito in ril.h).

Implementa la comunicazione RIL utilizzando i wakelock

I wakelock temporizzati vengono utilizzati nella comunicazione RIL in modo impreciso, il che influisce negativamente sulle prestazioni della batteria. In Android 7.x e versioni successive, puoi migliorare le prestazioni classificando le richieste RIL e aggiornando il codice per gestire i wakelock in modo diverso per i diversi tipi di richieste.

Classificare le richieste RIL

Le richieste RIL possono essere sollecitate o non sollecitate. I fornitori devono classificare ulteriormente le richieste sollecitate come una delle seguenti:

  • sincrono. Richieste che non richiedono molto tempo per rispondere. Ad esempio, RIL_REQUEST_GET_SIM_STATUS.
  • asincrono. Richieste che richiedono molto tempo per ricevere una risposta. Ad esempio, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS.

Le richieste RIL asincrone sollecitate possono richiedere molto tempo. Dopo aver ricevuto un ACK dal codice del fornitore, RIL Java rilascia il wakelock, il che potrebbe far passare il processore dell'app dallo stato di inattività a quello di sospensione. Quando la risposta è disponibile dal codice del fornitore, RIL Java (il processore dell'app) riacquisisce il wakelock, elabora la risposta e torna in modalità inattiva. Questo passaggio dallo stato di inattività alla sospensione e di nuovo all'inattività può consumare molta energia.

Se il tempo di risposta non è sufficiente, mantenere il wakelock e rimanere in idle per tutto il tempo necessario per rispondere può essere più efficiente dal punto di vista energetico rispetto all'entrare in stato di sospensione rilasciando il wakelock e riattivandosi all'arrivo della risposta. I fornitori devono utilizzare misurazioni del consumo energetico specifiche della piattaforma per determinare il valore di soglia del tempo T quando l'energia consumata rimanendo inattiva per l'intero periodo di tempo T è maggiore dell'energia consumata passando dallo stato di inattività alla sospensione e di nuovo all'inattività nello stesso periodo di tempo T. Quando è noto il tempo T, i comandi RIL che richiedono più tempo T possono essere classificati come asincroni e i comandi rimanenti come sincroni.

Scenari di comunicazione RIL

I seguenti diagrammi illustrano scenari comuni di comunicazione RIL e forniscono soluzioni per modificare il codice in modo da gestire le richieste RIL sollecitate e non sollecitate.

Nota:per i dettagli di implementazione delle funzioni utilizzate nei seguenti diagrammi, consulta i metodi acquireWakeLock(), decrementWakeLock() e clearWakeLock( in ril.cpp.

Scenario: richiesta RIL e risposta asincrona sollecitata

In questo scenario, se la risposta richiesta dall'intent integrato richiede molto tempo (ad es. una risposta a RIL_REQUEST_GET_AVAILABLE_NETWORKS), il wakelock viene mantenuto a lungo lato processore dell'app. I problemi con il modem possono anche comportare una lunga attesa.

Figura 1. Risposta asincrona sollecitata da RIL.

Soluzione 1: il modem mantiene il wakelock per la richiesta RIL e la risposta asincrona.

Figura 2. Wakelock mantenuto dal modem.
  1. La richiesta RIL viene inviata e il modem acquisisce il wakelock per elaborarla.
  2. Il modem invia un riconoscimento che fa sì che la parte Java decrementi il contatore di riattivazione e lo rilasci quando il valore del contatore è 0.

    Nota: la durata del timeout di wakelock per la sequenza richiesta-ack sarà inferiore a quella attualmente utilizzata perché l'ack deve essere ricevuto abbastanza rapidamente.

  3. Dopo aver elaborato la richiesta, il modem invia un interrupt al codice fornitore che acquisisce il wakelock e invia una risposta a ril.cpp, che a sua volta acquisisce il wakelock e invia una risposta al lato Java.
  4. Quando la risposta raggiunge il lato Java, viene acquisita la sospensione della riattivazione e una risposta viene restituita al chiamante.
  5. Dopo che la risposta è stata elaborata da tutti i moduli, viene inviato (tramite socket) un riconoscimento a ril.cpp, che rilascia il wakelock acquisito nel passaggio 3.

Soluzione 2: il modem non mantiene il wakelock e la risposta è rapida (richiesta e risposta RIL sincrone). Il comportamento sincrono e asincrono è hardcoded per un comando RIL specifico e viene deciso in base alle chiamate.

Figura 3. Wakelock non mantenuto dal modem.
  1. La richiesta RIL viene inviata chiamando acquireWakeLock() sul lato Java.
  2. Il codice fornitore non deve acquisire il wakelock e può elaborare la richiesta e rispondere rapidamente.
  3. Quando la risposta viene ricevuta dal lato Java, viene chiamato decrementWakeLock(), che decrementa il contatore di wakelock e rilascia il wakelock se il valore del contatore è 0.

Scenario: RIL unsolicited response

In questo scenario, le risposte non richieste RIL hanno un flag di tipo wakelock in che indica se è necessario acquisire un wakelock per la risposta del fornitore. Se il flag è impostato, viene impostato un wakelock temporizzato e la risposta viene inviata tramite un socket al lato Java. Quando il timer scade, il wakelock viene rilasciato. Il wakelock temporizzato potrebbe essere troppo lungo o troppo breve per le diverse risposte RIL non richieste.

Figura 4. Risposta non richiesta di RIL.

Soluzione:viene inviato un riconoscimento dal codice Java al lato nativo (ril.cpp) anziché mantenere un wakelock temporizzato sul lato nativo durante l'invio di una risposta non richiesta.

Figura 5. Utilizzo di ack anziché wakelock temporizzato.

Convalidare i wakelock riprogettati

Verifica che le chiamate RIL siano identificate come sincrone o asincrone. Poiché il consumo di energia della batteria può dipendere dall'hardware/dalla piattaforma, i fornitori devono eseguire alcuni test interni per scoprire se l'utilizzo della nuova semantica di wakelock per le chiamate asincrone comporta un risparmio di energia della batteria.