RIL-Refaktorierung

In Android 7.0 wurde die Radio Interface Layer (RIL) mit einer Reihe von Funktionen überarbeitet, um die RIL-Funktionalität zu verbessern. Für die Implementierung dieser Funktionen, die optional, aber empfehlenswert sind, sind Codeänderungen durch Partner erforderlich. Refactoring-Änderungen sind abwärtskompatibel, sodass frühere Implementierungen der refactorten Funktionen weiterhin funktionieren.

Das RIL-Refactoring umfasst die folgenden Verbesserungen:

  • RIL-Fehlercodes Aktiviert bestimmte Fehlercodes zusätzlich zum vorhandenen GENERIC_FAILURE-Code. Das hilft bei der Fehlerbehebung, da genauere Informationen zur Fehlerursache bereitgestellt werden.
  • RIL-Versionsverwaltung: Bietet genauere und einfacher zu konfigurierende Versionsinformationen.
  • RIL-Kommunikation über Wakelocks: Verbessert die Akkuleistung des Geräts.

Sie können alle oder einige der oben genannten Verbesserungen implementieren. Weitere Informationen finden Sie in den Codekommentaren zur RIL-Versionsverwaltung in https://android.googlesource.com/platform/hardware/ril/+/android16-release/include/telephony/ril.h.

Erweiterte RIL-Fehlercodes implementieren

Fast alle RIL-Anfrageaufrufe können als Reaktion auf einen Fehler den Fehlercode GENERIC_FAILURE zurückgeben. Dies ist ein Problem bei allen angeforderten Antworten, die von den OEMs zurückgegeben werden. Es kann schwierig sein, ein Problem anhand des Fehlerberichts zu beheben, wenn derselbe GENERIC_FAILURE-Fehlercode aus verschiedenen Gründen von RIL-Aufrufen zurückgegeben wird. Es kann viel Zeit in Anspruch nehmen, bis Anbieter herausfinden, welcher Teil des Codes einen GENERIC_FAILURE-Code zurückgegeben hat.

Unter Android 7.x und höher können OEMs einen eindeutigen Fehlercode für jeden Fehler zurückgeben, der derzeit als GENERIC_FAILURE kategorisiert ist. OEMs, die ihre benutzerdefinierten Fehlercodes nicht öffentlich preisgeben möchten, können Fehler als separate Gruppe von Ganzzahlen (z. B. 1 bis x) zurückgeben, die als OEM_ERROR_1 bis OEM_ERROR_X zugeordnet sind. Anbieter sollten dafür sorgen, dass jeder zurückgegebene maskierte Fehlercode im Code einem eindeutigen Fehlergrund zugeordnet ist. Die Verwendung bestimmter Fehlercodes kann das RIL-Debugging beschleunigen, wenn allgemeine Fehler vom OEM zurückgegeben werden. Es kann oft zu viel Zeit in Anspruch nehmen, die genaue Ursache eines GENERIC_FAILURE-Fehlercodes zu ermitteln (und manchmal ist es unmöglich).

Außerdem werden mit ril.h weitere Fehlercodes für die Enums RIL_LastCallFailCause und RIL_DataCallFailCause hinzugefügt, damit der Anbietercode die Rückgabe allgemeiner Fehler wie CALL_FAIL_ERROR_UNSPECIFIED und PDP_FAIL_ERROR_UNSPECIFIED vermeiden kann.

Erweiterte RIL-Fehlercodes validieren

Nachdem Sie neue Fehlercodes hinzugefügt haben, um den GENERIC_FAILURE-Code zu ersetzen, prüfen Sie, ob die neuen Fehlercodes vom RIL-Aufruf anstelle von GENERIC_FAILURE zurückgegeben werden.

Erweiterte RIL-Versionierung implementieren

Die RIL-Versionsverwaltung in älteren Android-Versionen war problematisch: Die Version selbst war ungenau, der Mechanismus zum Melden einer RIL-Version war unklar (was dazu führte, dass einige Anbieter eine falsche Version meldeten) und die Problemumgehung zum Schätzen der Version war ungenau.

Unter Android 7.x und höher werden in ril.h alle RIL-Versionswerte dokumentiert, die entsprechende RIL-Version beschrieben und alle Änderungen für diese Version aufgeführt. Wenn Änderungen vorgenommen werden, die einer RIL-Version entsprechen, müssen Anbieter ihre Version im Code aktualisieren und diese Version in RIL_REGISTER zurückgeben.

Erweiterte RIL-Versionsverwaltung validieren

Prüfen Sie, ob während RIL_REGISTER die RIL-Version zurückgegeben wird, die Ihrem RIL-Code entspricht (und nicht die RIL_VERSION, die in ril.h definiert ist).

RIL-Kommunikation mit Wakelocks implementieren

Zeitgesteuerte Wakelocks werden in der RIL-Kommunikation ungenau verwendet, was sich negativ auf die Akkuleistung auswirkt. In Android 7.x und höher können Sie die Leistung verbessern, indem Sie RIL-Anfragen klassifizieren und den Code so aktualisieren, dass Wakelocks für verschiedene Anfragetypen unterschiedlich behandelt werden.

RIL-Anfragen klassifizieren

RIL-Anfragen können entweder angefordert oder nicht angefordert werden. Anbieter sollten angeforderte Anfragen weiter in eine der folgenden Kategorien einteilen:

  • synchron. Anfragen, bei denen die Antwort nicht viel Zeit in Anspruch nimmt. Beispiel: RIL_REQUEST_GET_SIM_STATUS.
  • asynchron. Anfragen, bei denen es lange dauert, bis eine Antwort zurückgegeben wird. Beispiel: RIL_REQUEST_QUERY_AVAILABLE_NETWORKS.

Asynchrone angeforderte RIL-Anfragen können viel Zeit in Anspruch nehmen. Nachdem RIL Java eine Bestätigung vom Anbietercode erhalten hat, gibt es den Wakelock frei, wodurch der App-Prozessor möglicherweise vom Leerlauf- in den Ruhezustand wechselt. Wenn die Antwort über den Anbietercode verfügbar ist, ruft RIL Java (der App-Prozessor) den Wakelock noch einmal ab, verarbeitet die Antwort und kehrt dann in den Leerlauf zurück. Ein solcher Wechsel vom Leerlauf in den Ruhezustand und wieder zurück in den Leerlauf kann viel Strom verbrauchen.

Wenn die Reaktionszeit nicht lang genug ist, kann es energieeffizienter sein, den Wakelock beizubehalten und die gesamte Zeit, die für die Antwort benötigt wird, im Leerlauf zu bleiben, als in den Ruhezustand zu wechseln, indem der Wakelock freigegeben wird, und dann aufzuwachen, wenn die Antwort eintrifft. Anbieter sollten plattformspezifische Leistungsmessungen verwenden, um den Schwellenwert für die Zeit T zu bestimmen, wenn die Leistung, die durch den Leerlauf über die gesamte Zeit T verbraucht wird, größer ist als die Leistung, die durch den Wechsel vom Leerlauf in den Ruhezustand und zurück in den Leerlauf in derselben Zeit T verbraucht wird. Wenn die Zeit T bekannt ist, können RIL-Befehle, die länger als die Zeit T dauern, als asynchron und die verbleibenden Befehle als synchron klassifiziert werden.

RIL-Kommunikationsszenarien

Die folgenden Diagramme veranschaulichen gängige RIL-Kommunikationsszenarien und bieten Lösungen zum Ändern von Code, um angeforderte und nicht angeforderte RIL-Anfragen zu verarbeiten.

Hinweis:Implementierungsdetails zu den in den folgenden Diagrammen verwendeten Funktionen finden Sie in den Methoden acquireWakeLock(), decrementWakeLock() und clearWakeLock( in ril.cpp.

Szenario: RIL-Anfrage und angeforderte asynchrone Antwort

Wenn in diesem Szenario erwartet wird, dass die vom RIL angeforderte Antwort viel Zeit in Anspruch nimmt (z.B. eine Antwort auf RIL_REQUEST_GET_AVAILABLE_NETWORKS), wird der Wakelock auf der Seite des App-Prozessors für lange Zeit gehalten. Auch Modemprobleme können zu einer langen Wartezeit führen.

Abbildung 1. RIL hat eine asynchrone Antwort angefordert.

Lösung 1:Das Modem hält den Wakelock für die RIL-Anfrage und die asynchrone Antwort.

Abbildung 2. Wakelock wird vom Modem gehalten.
  1. Die RIL-Anfrage wird gesendet und das Modem ruft den Wakelock ab, um die Anfrage zu verarbeiten.
  2. Das Modem sendet eine Bestätigung, die dazu führt, dass die Java-Seite den Wakelock-Zähler dekrementiert und ihn freigibt, wenn der Zählerwert 0 ist.

    Hinweis:Das Wakelock-Zeitlimit für die Request-Ack-Sequenz wäre kürzer als das derzeit verwendete Zeitlimit, da die Bestätigung relativ schnell empfangen werden sollte.

  3. Nach der Verarbeitung der Anfrage sendet das Modem eine Unterbrechung an den Anbietercode, der den Wakelock erhält und eine Antwort an ril.cpp sendet. Diese Datei ruft wiederum den Wakelock ab und sendet eine Antwort an die Java-Seite.
  4. Wenn die Antwort die Java-Seite erreicht, wird der Wakelock abgerufen und eine Antwort an den Aufrufer zurückgegeben.
  5. Nachdem die Antwort von allen Modulen verarbeitet wurde, wird eine Bestätigung (über Socket) an ril.cpp gesendet, das dann den in Schritt 3 erworbenen Wakelock freigibt.

Lösung 2:Das Modem hält den Wakelock nicht und die Antwort erfolgt schnell (synchrone RIL-Anfrage und ‑Antwort). Das synchrone oder asynchrone Verhalten ist für einen bestimmten RIL-Befehl fest codiert und wird auf Anrufbasis entschieden.

Abbildung 3. Wakelock wird nicht vom Modem gehalten.
  1. Die RIL-Anfrage wird gesendet, indem acquireWakeLock() auf der Java-Seite aufgerufen wird.
  2. Für den Anbietercode ist kein Wakelock erforderlich. Er kann die Anfrage verarbeiten und schnell antworten.
  3. Wenn die Antwort auf der Java-Seite empfangen wird, wird decrementWakeLock() aufgerufen. Dadurch wird der Wakelock-Zähler verringert und der Wakelock freigegeben, wenn der Zählerwert 0 ist.

Szenario: Unaufgeforderte RIL-Antwort

In diesem Szenario haben RIL-Antworten, die nicht angefordert wurden, ein Wakelock-Typ-Flag im , das angibt, ob für die Anbieterantwort ein Wakelock erforderlich ist. Wenn das Flag gesetzt ist, wird ein zeitgesteuerter Wakelock festgelegt und die Antwort wird über einen Socket an die Java-Seite gesendet. Wenn der Timer abläuft, wird der Wakelock freigegeben. Das zeitgesteuerte Wakelock kann für verschiedene RIL-Antworten (Unsolicited Response) zu lang oder zu kurz sein.

Abbildung 4: RIL-Antwort ohne Aufforderung.

Lösung:Statt einen zeitgesteuerten Wakelock auf der nativen Seite zu halten, während eine unaufgeforderte Antwort gesendet wird, wird eine Bestätigung vom Java-Code an die native Seite (ril.cpp) gesendet.

Abbildung 5. Verwendung von „ack“ anstelle von zeitgesteuerten Wakelocks.

Neu gestaltete Wakelocks validieren

Prüfen Sie, ob RIL-Aufrufe als synchron oder asynchron identifiziert werden. Da der Akkuverbrauch hardware- und plattformabhängig sein kann, sollten Anbieter interne Tests durchführen, um herauszufinden, ob die Verwendung der neuen Wakelock-Semantik für asynchrone Aufrufe zu einer Reduzierung des Akkuverbrauchs führt.