Refactorización RIL

Android 7.0 refactorizó la capa de interfaz de radio (RIL) usando un conjunto de características para mejorar la funcionalidad de RIL. Se requieren cambios en el código de socio para implementar estas funciones, que son opcionales pero recomendadas. Los cambios de refactorización son compatibles con versiones anteriores, por lo que las implementaciones anteriores de las funciones refactorizadas siguen funcionando.

La refactorización de RIL incluye las siguientes mejoras:

  • Códigos de error RIL. Habilita códigos de error específicos además del código GENERIC_FAILURE existente. Esto ayuda en la solución de problemas de errores al proporcionar información más específica sobre la causa de los errores.
  • Versionado de RIL. Proporciona información de versión más precisa y fácil de configurar.
  • Comunicación RIL mediante wakelocks. Mejora el rendimiento de la batería del dispositivo.

Puede implementar cualquiera o todas las mejoras anteriores. Para obtener más detalles, consulte los comentarios del código sobre la versión de RIL en https://android.googlesource.com/platform/hardware/ril/+/main/include/telephony/ril.h .

Implementación de códigos de error RIL mejorados

Casi todas las llamadas de solicitud de RIL pueden devolver el código de error GENERIC_FAILURE en respuesta a un error. Este es un problema con todas las respuestas solicitadas devueltas por los OEM, lo que puede dificultar la depuración de un problema del informe de errores si las llamadas RIL devuelven el mismo código de error GENERIC_FAILURE por diferentes motivos. Los proveedores pueden tardar un tiempo considerable en identificar qué parte del código podría haber devuelto un código GENERIC_FAILURE .

En Android 7.x y versiones posteriores, los OEM pueden devolver un valor de código de error distinto asociado con cada error diferente que actualmente se categoriza como GENERIC_FAILURE . Los OEM que no desean revelar públicamente sus códigos de error personalizados pueden devolver errores como un conjunto distinto de números enteros (como 1 a x) asignados como OEM_ERROR_1 a OEM_ERROR_X . Los proveedores deben asegurarse de que cada código de error enmascarado devuelva los mapas a un motivo de error único en el código. El uso de códigos de error específicos puede acelerar la depuración de RIL cada vez que el OEM devuelve errores genéricos, ya que a menudo puede llevar demasiado tiempo identificar la causa exacta de un código de error GENERIC_FAILURE (y, a veces, es imposible averiguarlo).

Además, ril.h agrega más códigos de error para las enumeraciones RIL_LastCallFailCause y RIL_DataCallFailCause para que el código del proveedor pueda evitar la devolución de errores genéricos como CALL_FAIL_ERROR_UNSPECIFIED y PDP_FAIL_ERROR_UNSPECIFIED .

Validación de códigos de error RIL mejorados

Después de agregar nuevos códigos de error para reemplazar el código GENERIC_FAILURE , verifique que la llamada RIL devuelva los nuevos códigos de error en lugar de GENERIC_FAILURE .

Implementación de versiones mejoradas de RIL

La versión de RIL en versiones anteriores de Android era problemática: la versión en sí era imprecisa, el mecanismo para informar una versión de RIL no estaba claro (lo que hacía que algunos proveedores informaran una versión incorrecta) y la solución alternativa para estimar la versión era propensa a la inexactitud.

En Android 7.x y versiones posteriores, ril.h documenta todos los valores de la versión de RIL, describe la versión de RIL correspondiente y enumera todos los cambios para esa versión. Al realizar cambios que corresponden a una versión de RIL, los proveedores deben actualizar su versión en el código y devolver esa versión en RIL_REGISTER .

Validación de versiones mejoradas de RIL

Verifique que la versión RIL correspondiente a su código RIL se devuelva durante RIL_REGISTER (en lugar de la RIL_VERSION definida en ril.h ).

Implementación de la comunicación RIL mediante wakelocks

Los wakelocks temporizados se utilizan en la comunicación RIL de forma imprecisa, lo que afecta negativamente al rendimiento de la batería. En Android 7.x y versiones posteriores, puede mejorar el rendimiento clasificando las solicitudes RIL y actualizando el código para manejar los wakelocks de manera diferente para diferentes tipos de solicitudes.

Clasificación de solicitudes RIL

Las solicitudes de RIL pueden ser solicitadas o no solicitadas. Los proveedores deben clasificar las solicitudes solicitadas como una de las siguientes:

  • sincrónico Solicitudes que no toman un tiempo considerable para responder. Por ejemplo, RIL_REQUEST_GET_SIM_STATUS .
  • asincrónico Solicitudes que tardan un tiempo considerable en responder. Por ejemplo, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS .

Las solicitudes RIL solicitadas asíncronamente pueden tardar un tiempo considerable. Después de recibir un acuse de recibo del código del proveedor, RIL Java libera el wakelock, lo que puede hacer que el procesador de la aplicación pase del estado de inactividad al estado de suspensión. Cuando la respuesta está disponible desde el código del proveedor, RIL Java (el procesador de aplicaciones) vuelve a adquirir el wakelock, procesa la respuesta y luego vuelve a estar inactivo. Ese movimiento de inactivo a suspendido y luego inactivo puede consumir mucha energía.

Si el tiempo de respuesta no es lo suficientemente largo, mantener el wakelock y permanecer inactivo durante todo el tiempo que lleva responder puede ser más eficiente que pasar al estado de suspensión liberando el wakelock y despertando cuando llega la respuesta. Los proveedores deben usar mediciones de energía específicas de la plataforma para determinar el valor umbral del tiempo T cuando la energía consumida al permanecer inactivo durante todo el tiempo T es mayor que la energía consumida al pasar de inactivo a suspendido y a inactivo en el mismo tiempo T . Cuando se conoce el tiempo T , los comandos RIL que toman más tiempo T se pueden clasificar como asíncronos y los comandos restantes se clasifican como síncronos.

Escenarios de comunicación RIL

Los siguientes diagramas ilustran escenarios comunes de comunicación RIL y brindan soluciones para modificar el código para manejar solicitudes RIL solicitadas y no solicitadas.

Nota: Para obtener detalles sobre la implementación de las funciones utilizadas en los siguientes diagramas, consulte los métodos acquireWakeLock() , decrementWakeLock() y clearWakeLock( ) en ril.cpp .

Escenario: solicitud de RIL y respuesta asincrónica solicitada

En este escenario, si se espera que la respuesta solicitada por RIL tome un tiempo considerable (es decir, una respuesta a RIL_REQUEST_GET_AVAILABLE_NETWORKS ), el wakelock se mantiene durante mucho tiempo en el lado del procesador de la aplicación. Los problemas del módem también pueden resultar en una larga espera.

Figura 1. Respuesta asincrónica solicitada por RIL.

Solución 1: el módem mantiene el wakelock para la solicitud RIL y la respuesta asincrónica.

Figura 2. Wakelock retenido por módem.
  1. Se envía la solicitud RIL y el módem adquiere wakelock para procesar esa solicitud.
  2. El módem envía un reconocimiento que hace que el lado de Java disminuya el contador de wakelock y lo libere cuando el valor del contador sea 0.

    Nota: La duración del tiempo de espera de wakelock para la secuencia de confirmación de solicitud sería menor que la duración del tiempo de espera utilizado actualmente porque la confirmación debería recibirse con bastante rapidez.

  3. Después de procesar la solicitud, el módem envía una interrupción al código del proveedor que adquiere wakelock y envía una respuesta a ril.cpp, que a su vez adquiere wakelock y envía una respuesta al lado de Java.
  4. Cuando la respuesta llega al lado de Java, se adquiere wakelock y se devuelve una respuesta a la persona que llama.
  5. Después de que todos los módulos procesan la respuesta, se envía el reconocimiento (a través del socket) de nuevo a ril.cpp , que luego libera el wakelock adquirido en el paso 3.

Solución 2: el módem no retiene el wakelock y la respuesta es rápida (solicitud y respuesta RIL síncronas). El comportamiento síncrono frente a asíncrono está codificado para un comando RIL específico y se decide llamada por llamada.

Figura 3. Wakelock no retenido por módem.
  1. La solicitud de RIL se envía llamando a acquireWakeLock() en el lado de Java.
  2. El código del proveedor no necesita adquirir wakelock y puede procesar la solicitud y responder rápidamente.
  3. Cuando el lado de Java recibe la respuesta, se llama a decrementWakeLock() , que disminuye el contador de wakelock y libera wakelock si el valor del contador es 0.

Escenario: RIL respuesta no solicitada

En este escenario, las respuestas no solicitadas de RIL tienen un indicador de tipo wakelock que indica si es necesario adquirir un wakelock para la respuesta del proveedor. Si se establece el indicador, se establece un wakelock temporizado y la respuesta se envía a través de un socket al lado de Java. Cuando el temporizador expira, se libera el wakelock. El wakelock cronometrado podría ser demasiado largo o demasiado corto para diferentes respuestas no solicitadas de RIL.

Figura 4. Respuesta no solicitada de RIL.

Solución: se envía un acuse de recibo desde el código Java al lado nativo ( ril.cpp ) en lugar de mantener un wakelock cronometrado en el lado nativo mientras se envía una respuesta no solicitada.

Figura 5. Uso de ack en lugar de wakelock temporizado.

Validación de wakelocks rediseñados

Verifique que las llamadas RIL se identifiquen como sincrónicas o asincrónicas. Debido a que el consumo de energía de la batería puede depender del hardware/plataforma, los proveedores deben realizar algunas pruebas internas para averiguar si el uso de la nueva semántica de wakelock para llamadas asincrónicas genera ahorros de energía de la batería.