Refactorisation de RIL

Android 7.0 a refactorisé la couche d'interface radio (RIL) à l'aide d'un ensemble de fonctionnalités pour améliorer la fonctionnalité RIL. Les partenaires doivent modifier leur code pour implémenter ces fonctionnalités, qui sont facultatives, mais recommandées. Les modifications de refactoring sont rétrocompatibles. Les implémentations précédentes des fonctionnalités refactorisées continuent donc de fonctionner.

La refactorisation de RIL inclut les améliorations suivantes :

  • Codes d'erreur RIL. Active des codes d'erreur spécifiques en plus du code GENERIC_FAILURE existant. Cela permet de résoudre les problèmes d'erreur en fournissant des informations plus spécifiques sur leur cause.
  • Gestion des versions de la RIL. Fournit des informations sur les versions plus précises et plus faciles à configurer.
  • Communication RIL à l'aide de wakelocks. Améliore les performances de la batterie de l'appareil.

Vous pouvez implémenter une partie ou l'ensemble des améliorations ci-dessus. Pour en savoir plus, consultez les commentaires sur la gestion des versions RIL dans https://android.googlesource.com/platform/hardware/ril/+/android16-release/include/telephony/ril.h.

Implémenter des codes d'erreur RIL améliorés

Presque tous les appels de requête RIL peuvent renvoyer le code d'erreur GENERIC_FAILURE en cas d'erreur. Il s'agit d'un problème lié à toutes les réponses sollicitées renvoyées par les OEM, ce qui peut rendre difficile le débogage d'un problème à partir du rapport de bug si le même code d'erreur GENERIC_FAILURE est renvoyé par les appels RIL pour différentes raisons. Les fournisseurs peuvent mettre beaucoup de temps à identifier la partie du code qui aurait pu renvoyer un code GENERIC_FAILURE.

Dans Android 7.x et versions ultérieures, les OEM peuvent renvoyer une valeur de code d'erreur distincte associée à chaque erreur actuellement classée comme GENERIC_FAILURE. Les OEM qui ne souhaitent pas révéler publiquement leurs codes d'erreur personnalisés peuvent renvoyer les erreurs sous la forme d'un ensemble distinct d'entiers (par exemple, de 1 à x) mappés de OEM_ERROR_1 à OEM_ERROR_X. Les fournisseurs doivent s'assurer que chaque code d'erreur masqué renvoyé correspond à un motif d'erreur unique dans le code. L'utilisation de codes d'erreur spécifiques peut accélérer le débogage RIL chaque fois que des erreurs génériques sont renvoyées par l'OEM, car il peut souvent falloir trop de temps pour identifier la cause exacte d'un code d'erreur GENERIC_FAILURE (et parfois, il est impossible de le déterminer).

De plus, ril.h ajoute d'autres codes d'erreur pour les énumérations RIL_LastCallFailCause et RIL_DataCallFailCause afin que le code du fournisseur puisse éviter de renvoyer des erreurs génériques telles que CALL_FAIL_ERROR_UNSPECIFIED et PDP_FAIL_ERROR_UNSPECIFIED.

Valider les codes d'erreur RIL améliorés

Après avoir ajouté de nouveaux codes d'erreur pour remplacer le code GENERIC_FAILURE, vérifiez que les nouveaux codes d'erreur sont renvoyés par l'appel RIL au lieu de GENERIC_FAILURE.

Implémenter la gestion des versions RIL améliorée

La gestion des versions RIL dans les anciennes versions d'Android posait problème : la version elle-même était imprécise, le mécanisme de signalement d'une version RIL n'était pas clair (ce qui a conduit certains fournisseurs à signaler une version incorrecte) et la solution de contournement pour estimer la version était sujette à des inexactitudes.

Dans Android 7.x et versions ultérieures, ril.h documente toutes les valeurs de version RIL, décrit la version RIL correspondante et liste toutes les modifications apportées à cette version. Lorsqu'ils apportent des modifications correspondant à une version RIL, les fournisseurs doivent mettre à jour leur version dans le code et renvoyer cette version dans RIL_REGISTER.

Valider la gestion des versions RIL améliorée

Vérifiez que la version RIL correspondant à votre code RIL est renvoyée lors de RIL_REGISTER (plutôt que RIL_VERSION défini dans ril.h).

Implémenter la communication RIL à l'aide de wakelocks

Les wakelocks temporisés sont utilisés de manière imprécise dans la communication RIL, ce qui a un impact négatif sur les performances de la batterie. Dans Android 7.x et versions ultérieures, vous pouvez améliorer les performances en classifiant les requêtes RIL et en mettant à jour le code pour gérer les wakelocks différemment selon les types de requêtes.

Classer les demandes RIL

Les demandes RIL peuvent être sollicitées ou non sollicitées. Les fournisseurs doivent également classer les demandes sollicitées dans l'une des catégories suivantes :

  • synchrone. Les requêtes qui ne prennent pas beaucoup de temps à traiter. Par exemple, RIL_REQUEST_GET_SIM_STATUS.
  • asynchrone. Requêtes dont le traitement prend beaucoup de temps. Par exemple, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS.

Les requêtes RIL sollicitées asynchrones peuvent prendre beaucoup de temps. Après avoir reçu un accusé de réception du code du fournisseur, RIL Java libère le wakelock, ce qui peut entraîner le passage du processeur d'application de l'état inactif à l'état suspendu. Lorsque la réponse est disponible à partir du code du fournisseur, RIL Java (le processeur d'application) récupère le wakelock, traite la réponse, puis revient à l'état inactif. Ce type de transition peut consommer beaucoup d'énergie.

Si le temps de réponse n'est pas assez long, il peut être plus économe en énergie de maintenir le wakelock et de rester en veille pendant toute la durée de la réponse que de passer en état de suspension en libérant le wakelock et en se réveillant lorsque la réponse arrive. Les fournisseurs doivent utiliser des mesures de consommation d'énergie spécifiques à la plate-forme pour déterminer la valeur seuil de temps T lorsque la puissance consommée en restant en veille pendant toute la durée T est supérieure à la puissance consommée en passant de la veille à la suspension, puis à la veille pendant la même durée T. Lorsque le temps T est connu, les commandes RIL qui prennent plus de temps que T peuvent être classées comme asynchrones, et les commandes restantes comme synchrones.

Scénarios de communication RIL

Les schémas suivants illustrent des scénarios de communication RIL courants et fournissent des solutions pour modifier le code afin de gérer les requêtes RIL sollicitées et non sollicitées.

Remarque : Pour en savoir plus sur l'implémentation des fonctions utilisées dans les schémas suivants, consultez les méthodes acquireWakeLock(), decrementWakeLock() et clearWakeLock( dans ril.cpp.

Scénario : Requête RIL et réponse asynchrone sollicitée

Dans ce scénario, si la réponse sollicitée par RIL est censée prendre beaucoup de temps (c'est-à-dire une réponse à RIL_REQUEST_GET_AVAILABLE_NETWORKS), le wakelock est maintenu pendant une longue période du côté du processeur de l'application. Des problèmes de modem peuvent également entraîner une longue attente.

Figure 1. Réponse asynchrone sollicitée par RIL.

Solution 1 : Le modem conserve le wakelock pour la requête RIL et la réponse asynchrone.

Figure 2. Wakelock détenu par le modem.
  1. La requête RIL est envoyée et le modem acquiert le wakelock pour traiter cette requête.
  2. Le modem envoie un accusé de réception qui entraîne la diminution du compteur de wakelock côté Java et sa libération lorsque la valeur du compteur est égale à 0.

    Remarque : La durée du délai d'inactivité du wakelock pour la séquence de demande-accusé de réception serait inférieure à la durée du délai d'inactivité actuellement utilisée, car l'accusé de réception devrait être reçu assez rapidement.

  3. Après avoir traité la requête, le modem envoie une interruption au code du fournisseur qui acquiert le wakelock et envoie une réponse à ril.cpp, qui à son tour acquiert le wakelock et envoie une réponse au côté Java.
  4. Lorsque la réponse atteint le côté Java, le wakelock est acquis et une réponse est renvoyée à l'appelant.
  5. Une fois la réponse traitée par tous les modules, un accusé de réception est renvoyé (via le socket) à ril.cpp, qui libère ensuite le wakelock acquis à l'étape 3.

Solution 2 : Le modem ne détient pas le wakelock et la réponse est rapide (requête et réponse RIL synchrones). Le comportement synchrone ou asynchrone est codé en dur pour une commande RIL spécifique et décidé sur une base d'appel par appel.

Figure 3. Le wakelock n'est pas détenu par le modem.
  1. La requête RIL est envoyée en appelant acquireWakeLock() côté Java.
  2. Le code du fournisseur n'a pas besoin d'acquérir de wakelock et peut traiter la requête et y répondre rapidement.
  3. Lorsque la réponse est reçue par le côté Java, decrementWakeLock() est appelé, ce qui diminue le compteur de wakelock et libère le wakelock si la valeur du compteur est égale à 0.

Scénario : réponse non sollicitée RIL

Dans ce scénario, les réponses non sollicitées RIL comportent un indicateur de type wakelock dans le qui indique si un wakelock doit être acquis pour la réponse du fournisseur. Si l'indicateur est défini, un wakelock temporisé est défini et la réponse est envoyée via un socket côté Java. Lorsque le minuteur expire, le wakelock est libéré. Le wakelock temporisé peut être trop long ou trop court pour différentes réponses non sollicitées RIL.

Figure 4. Réponse non sollicitée du RIL.

Solution : un accusé de réception est envoyé du code Java au côté natif (ril.cpp) au lieu de maintenir un wakelock temporisé du côté natif lors de l'envoi d'une réponse non sollicitée.

Figure 5 : Utilisation d'ack au lieu de wakelock temporisé.

Valider les wakelocks repensés

Vérifiez que les appels RIL sont identifiés comme synchrones ou asynchrones. Étant donné que la consommation d'énergie de la batterie peut dépendre du matériel/de la plate-forme, les fournisseurs doivent effectuer des tests internes pour déterminer si l'utilisation de la nouvelle sémantique de wakelock pour les appels asynchrones permet de réaliser des économies d'énergie de la batterie.