Refactorización de RIL

Android 7.0 refactorizó la capa de interfaz de radio (RIL) con un conjunto de funciones para mejorar su funcionalidad. Se requieren cambios en el código del socio para implementar estas funciones, que son opcionales, pero se recomienda hacerlo. Los cambios de refactorización son retrocompatibles, 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 de RIL. Habilita códigos de error específicos además del código GENERIC_FAILURE existente. Esto ayuda a solucionar problemas de errores, ya que proporciona información más específica sobre la causa de los errores.
  • Control de versiones de RIL. Proporciona información de versión más precisa y fácil de configurar.
  • Comunicación de RIL con bloqueos de activación. Mejora el rendimiento de la batería del dispositivo.

Puedes implementar todas las mejoras anteriores o solo algunas. Para obtener más detalles, consulta los comentarios del código sobre el control de versiones de RIL en https://android.googlesource.com/platform/hardware/ril/+/android16-release/include/telephony/ril.h.

Implementa códigos de error de 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 que devuelven los OEM, lo que puede dificultar la depuración de un problema a partir del informe de errores si las llamadas a RIL devuelven el mismo código de error GENERIC_FAILURE por diferentes motivos. Los proveedores pueden tardar mucho tiempo 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 a cada error diferente que actualmente se clasifica como GENERIC_FAILURE. Los OEM que no deseen revelar públicamente sus códigos de error personalizados pueden devolver errores como un conjunto distinto de números enteros (por ejemplo, del 1 a X) asignados como OEM_ERROR_1 a OEM_ERROR_X. Los proveedores deben asegurarse de que cada código de error enmascarado que se devuelva se asigne a un motivo de error único en el código. Usar códigos de error específicos puede acelerar la depuración de la RIL cuando 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 determinarla).

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

Valida los códigos de error de RIL mejorados

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

Implementa el control de versiones de RIL mejorado

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

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 de esa versión. Cuando realicen cambios que correspondan 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.

Valida el control de versiones de RIL mejorado

Verifica que la versión de RIL correspondiente a tu código de RIL se devuelva durante RIL_REGISTER (en lugar de RIL_VERSION definido en ril.h).

Implementa la comunicación de RIL con bloqueos de activación

Los wakelocks cronometrados se usan en la comunicación de RIL de forma imprecisa, lo que afecta negativamente el rendimiento de la batería. En Android 7.x y versiones posteriores, puedes mejorar el rendimiento clasificando las solicitudes de RIL y actualizando el código para controlar los bloqueos de activación de forma diferente para los distintos tipos de solicitudes.

Clasifica las solicitudes de RIL

Las solicitudes de RIL pueden ser solicitadas o no solicitadas. Además, los proveedores deben clasificar las solicitudes como una de las siguientes:

  • síncrono. Solicitudes que no tardan demasiado en responder. Por ejemplo, RIL_REQUEST_GET_SIM_STATUS.
  • asíncrono. Solicitudes que tardan mucho en responderse Por ejemplo, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS.

Las solicitudes de RIL asíncronas pueden tardar bastante tiempo. Después de recibir una confirmación del código del proveedor, el RIL Java libera el bloqueo de activación, lo que podría hacer que el procesador de la app pase del estado inactivo al de suspensión. Cuando la respuesta está disponible en el código del proveedor, RIL Java (el procesador de la app) vuelve a adquirir el bloqueo de activación, procesa la respuesta y, luego, vuelve al estado de inactividad. Este proceso de pasar de inactivo a suspendido y de nuevo a inactivo puede consumir mucha energía.

Si el tiempo de respuesta no es lo suficientemente largo, mantener el bloqueo de activación y permanecer en estado de inactividad durante todo el tiempo que lleva responder puede ser más eficiente en cuanto al consumo de energía que entrar en estado de suspensión liberando el bloqueo de activación y activándose cuando llega la respuesta. Los proveedores deben usar mediciones de energía específicas de la plataforma para determinar el valor de umbral del tiempo T cuando la energía que se consume por permanecer en inactividad durante todo el tiempo T es mayor que la energía que se consume por pasar de inactividad a suspensión y a inactividad en el mismo tiempo T. Cuando se conoce el tiempo T, los comandos de RIL que tardan más que el tiempo T se pueden clasificar como asíncronos y los comandos restantes se pueden clasificar como síncronos.

Situaciones de comunicación de RIL

En los siguientes diagramas, se ilustran situaciones comunes de comunicación de la RIL y se proporcionan soluciones para modificar el código de modo que controle las solicitudes de la RIL solicitadas y no solicitadas.

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

Situación: Solicitud de RIL y respuesta asíncrona solicitada

En esta situación, si se espera que la respuesta solicitada de RIL tarde un tiempo considerable (es decir, una respuesta a RIL_REQUEST_GET_AVAILABLE_NETWORKS), el bloqueo de activación se mantiene durante un tiempo prolongado en el procesador de la app. Los problemas con el módem también pueden generar una espera prolongada.

Figura 1: Respuesta asíncrona solicitada por RIL.

Solución 1: El módem mantiene el bloqueo de activación para la solicitud de RIL y la respuesta asíncrona.

Figura 2: Bloqueo de activación que mantiene el módem.
  1. Se envía la solicitud de RIL y el módem adquiere el bloqueo de activación para procesar esa solicitud.
  2. El módem envía una confirmación que hace que el lado de Java disminuya el contador de bloqueo de activación y lo libere cuando el valor del contador es 0.

    Nota: La duración del tiempo de espera de wakelock para la secuencia de solicitud y confirmación sería menor que la duración del tiempo de espera que se usa actualmente, ya que 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 el bloqueo de activación y envía una respuesta a ril.cpp, que, a su vez, adquiere el bloqueo de activación y envía una respuesta al lado de Java.
  4. Cuando la respuesta llega al lado de Java, se adquiere el bloqueo de activación y se devuelve una respuesta al llamador.
  5. Después de que todos los módulos procesan la respuesta, se envía una confirmación de recepción (a través del socket) a ril.cpp, que luego libera el bloqueo de activación adquirido en el paso 3.

Solución 2: El módem no mantiene el bloqueo de activación y la respuesta es rápida (solicitud y respuesta de RIL síncronas). El comportamiento síncrono frente al asíncrono está codificado de forma rígida para un comando de RIL específico y se decide en cada llamada.

Figura 3: El módem no mantiene el bloqueo de activación.
  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 un bloqueo de activación 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 el wakelock si el valor del contador es 0.

Situación: Respuesta no solicitada de RIL

En este caso, las respuestas no solicitadas de RIL tienen una marca de tipo de bloqueo de activación en la que indica si se debe adquirir un bloqueo de activación para la respuesta del proveedor. Si se configura la marca, se establece un bloqueo de activación cronometrado y la respuesta se envía a través de un socket al lado de Java. Cuando vence el temporizador, se libera el bloqueo de activación. El bloqueo de activación cronometrado podría ser demasiado largo o demasiado corto para diferentes respuestas no solicitadas de RIL.

Figura 4: Es una 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 bloqueo de activación cronometrado en el lado nativo mientras se envía una respuesta no solicitada.

Figure 5: Se usa ack en lugar de timed wakelock.

Valida los wakelocks rediseñados

Verifica que las llamadas de RIL se identifiquen como síncronas o asíncronas. Dado que el consumo de batería puede depender del hardware o la plataforma, los proveedores deben realizar algunas pruebas internas para determinar si el uso de la nueva semántica de wakelock para las llamadas asíncronas genera un ahorro de batería.