Refaktoryzacja RIL

W systemie Android 7.0 dokonano refaktoryzacji warstwy interfejsu radiowego (RIL), korzystając z zestawu funkcji poprawiających funkcjonalność RIL. Aby wdrożyć te funkcje, wymagane są zmiany w kodzie partnera. Są one opcjonalne, ale zalecane. Zmiany refaktoryzacji są kompatybilne wstecz, więc wcześniejsze implementacje refaktoryzowanych funkcji nadal działają.

Refaktoryzacja RIL obejmuje następujące ulepszenia:

  • Kody błędów RIL. Włącza określone kody błędów oprócz istniejącego kodu GENERIC_FAILURE . Pomaga to w rozwiązywaniu problemów, dostarczając bardziej szczegółowych informacji o przyczynie błędów.
  • Wersja RIL. Zapewnia dokładniejsze i łatwiejsze w konfiguracji informacje o wersji.
  • Komunikacja RIL za pomocą wakelocków. Poprawia wydajność baterii urządzenia.

Możesz wdrożyć dowolne lub wszystkie z powyższych ulepszeń. Aby uzyskać więcej informacji, zobacz komentarze do kodu dotyczące wersji RIL w https://android.googlesource.com/platform/hardware/ril/+/main/include/telephony/ril.h .

Implementowanie ulepszonych kodów błędów RIL

Prawie wszystkie wywołania żądań RIL mogą zwrócić kod błędu GENERIC_FAILURE w odpowiedzi na błąd. Jest to problem dotyczący wszystkich żądanych odpowiedzi zwracanych przez producentów OEM, co może utrudniać debugowanie problemu na podstawie raportu o błędzie, jeśli ten sam kod błędu GENERIC_FAILURE jest zwracany przez wywołania RIL z różnych powodów. Nawet określenie, która część kodu mogła zwrócić kod GENERIC_FAILURE , może zająć dostawcy sporo czasu.

W systemie Android 7.x i nowszych wersjach producenci OEM mogą zwrócić odrębną wartość kodu błędu powiązaną z każdym innym błędem, który jest obecnie sklasyfikowany jako GENERIC_FAILURE . Producenci OEM, którzy nie chcą publicznie ujawniać swoich niestandardowych kodów błędów, mogą zwracać błędy w postaci odrębnego zestawu liczb całkowitych (takich jak 1 do x) odwzorowanych jako OEM_ERROR_1 na OEM_ERROR_X . Dostawcy powinni upewnić się, że każdy taki zamaskowany kod błędu zwrócony jest w oparciu o unikalną przyczynę błędu w kodzie. Używanie określonych kodów błędów może przyspieszyć debugowanie RIL za każdym razem, gdy producent OEM zwraca błędy ogólne, ponieważ często identyfikacja dokładnej przyczyny kodu błędu GENERIC_FAILURE może zająć zbyt dużo czasu (a czasami jest to niemożliwe do ustalenia).

Ponadto ril.h dodaje więcej kodów błędów dla wyliczeń RIL_LastCallFailCause i RIL_DataCallFailCause , dzięki czemu kod dostawcy może uniknąć zwracania błędów ogólnych, takich jak CALL_FAIL_ERROR_UNSPECIFIED i PDP_FAIL_ERROR_UNSPECIFIED .

Sprawdzanie poprawności ulepszonych kodów błędów RIL

Po dodaniu nowych kodów błędów w celu zastąpienia kodu GENERIC_FAILURE sprawdź, czy wywołanie RIL zwraca nowe kody błędów zamiast GENERIC_FAILURE .

Implementowanie ulepszonej wersji RIL

Wersjonowanie RIL w starszych wersjach Androida było problematyczne: sama wersja była nieprecyzyjna, mechanizm zgłaszania wersji RIL był niejasny (co powodowało, że niektórzy dostawcy zgłaszali niepoprawną wersję), a obejście polegające na szacowaniu wersji było podatne na niedokładności.

W systemie Android 7.x i nowszych ril.h dokumentuje wszystkie wartości wersji RIL, opisuje odpowiednią wersję RIL i wyświetla listę wszystkich zmian dla tej wersji. Dokonując zmian odpowiadających wersji RIL, dostawcy muszą zaktualizować swoją wersję w kodzie i zwrócić tę wersję w RIL_REGISTER .

Sprawdzanie poprawności ulepszonej wersji RIL

Sprawdź, czy wersja RIL odpowiadająca Twojemu kodowi RIL jest zwracana podczas RIL_REGISTER (a nie RIL_VERSION zdefiniowana w ril.h ).

Implementacja komunikacji RIL z wykorzystaniem wakelocków

Wakelocki czasowe są wykorzystywane w komunikacji RIL w sposób nieprecyzyjny, co negatywnie wpływa na wydajność baterii. W systemie Android 7.x i nowszych wersjach można poprawić wydajność, klasyfikując żądania RIL i aktualizując kod, aby obsługiwał wakelocki w różny sposób dla różnych typów żądań.

Klasyfikacja żądań RIL

Żądania RIL mogą być zamówione lub niezamówione. Dostawcy powinni dodatkowo klasyfikować zamówione żądania jako jedną z następujących kategorii:

  • synchroniczny . Żądania, na które odpowiedź nie wymaga dużo czasu. Na przykład RIL_REQUEST_GET_SIM_STATUS .
  • asynchroniczny . Żądania, na które odpowiedź zajmuje dużo czasu. Na przykład RIL_REQUEST_QUERY_AVAILABLE_NETWORKS .

Asynchroniczne żądane żądania RIL mogą zająć dużo czasu. Po otrzymaniu potwierdzenia od kodu dostawcy RIL Java zwalnia funkcję wakelock, która może spowodować przejście procesora aplikacji ze stanu bezczynności do stanu zawieszenia. Gdy odpowiedź jest dostępna z kodu dostawcy, RIL Java (procesor aplikacji) ponownie uzyskuje wakelock, przetwarza odpowiedź, a następnie powraca do stanu bezczynności. Takie przejście z biegu jałowego do zawieszenia do biegu jałowego może zużywać dużo energii.

Jeśli czas reakcji nie jest wystarczająco długi, przytrzymanie blokady wakelock i pozostawanie w stanie bezczynności przez cały czas potrzebny na reakcję może być bardziej energooszczędne niż przejście w stan wstrzymania poprzez zwolnienie blokady wakelock i wybudzenie po nadejściu odpowiedzi. Dostawcy powinni stosować pomiary mocy specyficzne dla platformy, aby określić wartość progową czasu T , gdy moc zużywana podczas pozostawania w stanie bezczynności przez cały czas T jest większa niż moc zużywana podczas przejścia ze stanu bezczynności do zawieszenia i do stanu bezczynności w tym samym czasie T . Gdy znany jest czas T , polecenia RIL, które trwają dłużej niż czas T , można sklasyfikować jako asynchroniczne, a pozostałe polecenia sklasyfikować jako synchroniczne.

Scenariusze komunikacji RIL

Poniższe diagramy ilustrują typowe scenariusze komunikacji RIL i przedstawiają rozwiązania umożliwiające modyfikację kodu w celu obsługi żądań RIL zamówionych i niezamówionych.

Uwaga: Szczegóły implementacji funkcji używanych na poniższych diagramach można znaleźć w metodach acquireWakeLock() , decrementWakeLock() i clearWakeLock( ) w ril.cpp .

Scenariusz: żądanie RIL i żądana odpowiedź asynchroniczna

W tym scenariuszu, jeśli oczekuje się, że odpowiedź na żądanie RIL zajmie dużo czasu (tj. odpowiedź na RIL_REQUEST_GET_AVAILABLE_NETWORKS ), wakelock jest utrzymywany przez długi czas po stronie procesora aplikacji. Problemy z modemem mogą również powodować długie oczekiwanie.

Rysunek 1. RIL zażądał odpowiedzi asynchronicznej.

Rozwiązanie 1: Modem blokuje wakelock dla żądania RIL i odpowiedzi asynchronicznej.

Rysunek 2. Wakelock utrzymywany przez modem.
  1. Żądanie RIL zostaje wysłane, a modem uzyskuje funkcję wakelock w celu przetworzenia tego żądania.
  2. Modem wysyła potwierdzenie, które powoduje, że strona Java zmniejsza licznik wakelocków i zwalnia go, gdy wartość licznika wynosi 0.

    Uwaga: Limit czasu wakelock dla sekwencji żądania potwierdzenia będzie mniejszy niż aktualnie używany limit czasu, ponieważ potwierdzenie powinno zostać odebrane dość szybko.

  3. Po przetworzeniu żądania modem wysyła przerwanie do kodu dostawcy, który pobiera wakelock i wysyła odpowiedź do ril.cpp, który z kolei uzyskuje wakelock i wysyła odpowiedź do strony Java.
  4. Gdy odpowiedź dotrze do strony Java, następuje aktywacja wakelocka i odpowiedź jest zwracana do osoby wywołującej.
  5. Po przetworzeniu odpowiedzi przez wszystkie moduły, potwierdzenie jest wysyłane (przez gniazdo) z powrotem do ril.cpp , który następnie zwalnia wakelock uzyskany w kroku 3.

Rozwiązanie 2: Modem nie utrzymuje wakelocka, a odpowiedź jest szybka (synchroniczne żądanie i odpowiedź RIL). Zachowanie synchroniczne i asynchroniczne jest zakodowane na stałe dla konkretnego polecenia RIL i ustalane na podstawie połączenia po wywołaniu.

Rysunek 3. Wakelock nie jest utrzymywany przez modem.
  1. Żądanie RIL jest wysyłane poprzez wywołanie metody acquireWakeLock() po stronie Java.
  2. Kod dostawcy nie musi nabywać wakelocka i może przetworzyć żądanie i szybko odpowiedzieć.
  3. Po odebraniu odpowiedzi przez stronę Java wywoływana jest decrementWakeLock() , która zmniejsza licznik wakelocków i zwalnia wakelock, jeśli wartość licznika wynosi 0.

Scenariusz: niechciana odpowiedź RIL

W tym scenariuszu niezamówione odpowiedzi RIL mają flagę typu wakelock, która wskazuje, czy należy uzyskać wakelock w celu odpowiedzi dostawcy. Jeśli flaga jest ustawiona, ustawiany jest czasowy wakelock i odpowiedź jest wysyłana przez gniazdo do strony Java. Kiedy upłynie czas timera, wakelock zostaje zwolniony. Czasowy wakelock może być za długi lub za krótki dla różnych niechcianych odpowiedzi RIL.

Rysunek 4. Niechciana odpowiedź RIL.

Rozwiązanie: Potwierdzenie jest wysyłane z kodu Java do strony natywnej ( ril.cpp ) zamiast wstrzymywać czasową blokadę wakelock po stronie natywnej podczas wysyłania niechcianej odpowiedzi.

Rysunek 5. Używanie potwierdzenia zamiast czasowego wakelocka.

Sprawdzanie przeprojektowanych wakelocków

Sprawdź, czy wywołania RIL są identyfikowane jako synchroniczne lub asynchroniczne. Ponieważ zużycie energii baterii może zależeć od sprzętu/platformy, dostawcy powinni przeprowadzić wewnętrzne testy, aby dowiedzieć się, czy użycie nowej semantyki wakelock dla połączeń asynchronicznych prowadzi do oszczędności energii baterii.