Refaktoryzacja RIL

W Androidzie 7.0 przeprojektowano warstwę interfejsu radiowego (RIL) za pomocą zestawu funkcji, aby poprawić jej działanie. Aby wdrożyć te funkcje, które są opcjonalne, ale zalecane, należy wprowadzić zmiany w kodzie partnera. Zmiany w refaktoryzacji są wstecznie kompatybilne, więc wcześniejsze implementacje refaktoryzowanych funkcji nadal działają.

Refaktoryzacja RIL obejmuje te ulepszenia:

  • Kody błędów RIL Umożliwia korzystanie z określonych kodów błędów oprócz istniejącego kodu GENERIC_FAILURE. Ułatwia to rozwiązywanie problemów, ponieważ zawiera bardziej szczegółowe informacje o przyczynach błędów.
  • Wersje RIL Zawiera dokładniejsze i łatwiejsze do skonfigurowania informacje o wersji.
  • Komunikacja RIL z użyciem blokad wybudzania. Poprawia wydajność baterii urządzenia.

Możesz wdrożyć wszystkie wymienione wyżej ulepszenia lub tylko niektóre z nich. Więcej informacji znajdziesz w komentarzach do kodu dotyczących wersji RIL w https://android.googlesource.com/platform/hardware/ril/+/android16-release/include/telephony/ril.h.

Wdrażanie rozszerzonych kodów błędów RIL

Prawie wszystkie wywołania żądań RIL mogą w odpowiedzi na błąd zwracać kod błędu GENERIC_FAILURE. Jest to problem ze wszystkimi odpowiedziami na żądanie zwracanymi 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. Określenie, która część kodu mogła zwrócić kod GENERIC_FAILURE, może zająć dostawcom sporo czasu.

W Androidzie 7.x i nowszym producenci OEM mogą zwracać odrębną wartość kodu błędu powiązaną z każdym 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 jako odrębny zestaw liczb całkowitych (np. od 1 do x) mapowanych jako OEM_ERROR_1 do OEM_ERROR_X. Dostawcy powinni zadbać o to, aby każdy zwrócony zamaskowany kod błędu był powiązany z unikalną przyczyną błędu w kodzie. Używanie konkretnych kodów błędów może przyspieszyć debugowanie RIL, gdy producent OEM zwraca ogólne błędy, ponieważ zidentyfikowanie dokładnej przyczyny kodu błędu GENERIC_FAILURE może często zająć zbyt dużo czasu (a czasami jest to niemożliwe).

Dodatkowo ril.h dodaje więcej kodów błędów do wyliczeń RIL_LastCallFailCauseRIL_DataCallFailCause, dzięki czemu kod dostawcy może uniknąć zwracania ogólnych błędów, takich jak CALL_FAIL_ERROR_UNSPECIFIEDPDP_FAIL_ERROR_UNSPECIFIED.

Weryfikowanie rozszerzonych kodów błędów RIL

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

Wdrażanie ulepszonej wersji RIL

W starszych wersjach Androida wersjonowanie RIL było problematyczne: sama wersja była nieprecyzyjna, mechanizm raportowania wersji RIL był niejasny (co powodowało, że niektórzy dostawcy zgłaszali nieprawidłową wersję), a obejście problemu polegające na szacowaniu wersji było podatne na błędy.

W Androidzie 7.x i nowszych ril.h dokumentuje wszystkie wartości wersji RIL, opisuje odpowiednią wersję RIL i wymienia wszystkie zmiany w tej wersji. W przypadku wprowadzania zmian odpowiadających wersji RIL dostawcy muszą zaktualizować swoją wersję w kodzie i zwrócić ją w RIL_REGISTER.

Weryfikowanie ulepszonego określania wersji RIL

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

Implementowanie komunikacji RIL za pomocą blokad wybudzania

W komunikacji RIL używane są wybudzenia czasowe w nieprecyzyjny sposób, co negatywnie wpływa na wydajność baterii. W Androidzie 7.x i nowszych możesz poprawić wydajność, klasyfikując żądania RIL i aktualizując kod, aby inaczej obsługiwać blokady wybudzania w przypadku różnych typów żądań.

Klasyfikowanie żądań RIL

Żądania RIL mogą być wywołane lub niewywołane. Dostawcy powinni dodatkowo zaklasyfikować żądania do jednej z tych kategorii:

  • synchroniczne. Żądania, na które nie trzeba długo odpowiadać. Na przykład: RIL_REQUEST_GET_SIM_STATUS.
  • asynchroniczne. Żądania, na które odpowiedź zajmuje dużo czasu. Na przykład: RIL_REQUEST_QUERY_AVAILABLE_NETWORKS.

Asynchroniczne żądania RIL mogą trwać dość długo. Po otrzymaniu potwierdzenia z kodu dostawcy RIL Java zwalnia blokadę wybudzania, co może spowodować przejście procesora aplikacji ze stanu bezczynności do stanu zawieszenia. Gdy odpowiedź jest dostępna w kodzie dostawcy, RIL Java (procesor aplikacji) ponownie uzyskuje blokadę wybudzania, przetwarza odpowiedź, a następnie wraca do stanu bezczynności. Takie przechodzenie z trybu bezczynności do trybu uśpienia i z powrotem do trybu bezczynności może zużywać dużo energii.

Jeśli czas odpowiedzi jest zbyt krótki, utrzymywanie blokady wybudzania i pozostawanie w stanie bezczynności przez cały czas potrzebny na odpowiedź może być bardziej energooszczędne niż przejście w stan wstrzymania przez zwolnienie blokady wybudzania i wybudzenie po nadejściu odpowiedzi. Dostawcy powinni używać pomiarów mocy specyficznych dla platformy, aby określić wartość progową czasu T, gdy moc zużywana przez pozostawanie w stanie bezczynności przez cały czas T jest większa niż moc zużywana przez przejście ze stanu bezczynności do stanu wstrzymania i z powrotem do stanu bezczynności w tym samym czasie T. Gdy czas T jest znany, polecenia RIL, których wykonanie zajmuje więcej czasu niż T, można zaklasyfikować jako asynchroniczne, a pozostałe polecenia jako synchroniczne.

Scenariusze komunikacji RIL

Poniższe diagramy ilustrują typowe scenariusze komunikacji RIL i zawierają rozwiązania umożliwiające modyfikowanie kodu w celu obsługi żądań RIL wysyłanych i niewysyłanych.

Uwaga: szczegóły implementacji funkcji użytych na poniższych diagramach znajdziesz w metodach acquireWakeLock(), decrementWakeLock()clearWakeLock(ril.cpp.

Scenariusz: żądanie RIL i wywołana odpowiedź asynchroniczna

W tym scenariuszu, jeśli oczekiwana odpowiedź RIL ma zająć sporo czasu (np. odpowiedź na RIL_REQUEST_GET_AVAILABLE_NETWORKS), blokada wybudzania jest utrzymywana przez długi czas po stronie procesora aplikacji. Problemy z modemem również mogą spowodować długie oczekiwanie.

Rysunek 1. RIL solicited asynchronous response.

Rozwiązanie 1: modem utrzymuje blokadę wybudzania na potrzeby żądania RIL i odpowiedzi asynchronicznej.

Rysunek 2. Blokada wybudzenia utrzymywana przez modem.
  1. Wysyłane jest żądanie RIL, a modem uzyskuje blokadę wybudzania, aby je przetworzyć.
  2. Modem wysyła potwierdzenie, które powoduje zmniejszenie licznika blokady wybudzania po stronie Javy i zwolnienie go, gdy wartość licznika wynosi 0.

    Uwaga: czas oczekiwania na wybudzenie w przypadku sekwencji żądanie-potwierdzenie będzie krótszy niż obecnie używany czas oczekiwania, ponieważ potwierdzenie powinno być odbierane dość szybko.

  3. Po przetworzeniu żądania modem wysyła przerwanie do kodu dostawcy, który uzyskuje blokadę wybudzania i wysyła odpowiedź do pliku ril.cpp, który z kolei uzyskuje blokadę wybudzania i wysyła odpowiedź do strony Java.
  4. Gdy odpowiedź dotrze do części Java, zostanie uzyskana blokada wybudzania i odpowiedź zostanie zwrócona elementowi wywołującemu.
  5. Po przetworzeniu odpowiedzi przez wszystkie moduły do ril.cpp jest wysyłane potwierdzenie (przez gniazdo), które zwalnia blokadę wybudzania uzyskaną w kroku 3.

Rozwiązanie 2: modem nie blokuje wybudzania, a odpowiedź jest szybka (synchroniczne żądanie i odpowiedź RIL). Zachowanie synchroniczne i asynchroniczne jest na stałe przypisane do konkretnego polecenia RIL i określane na podstawie poszczególnych wywołań.

Rysunek 3. Wakelock not held by modem.
  1. Żądanie RIL jest wysyłane przez wywołanie funkcji acquireWakeLock() po stronie Javy.
  2. Kod dostawcy nie musi uzyskiwać blokady wybudzania i może przetworzyć żądanie oraz szybko na nie odpowiedzieć.
  3. Gdy odpowiedź zostanie odebrana po stronie Javy, wywoływana jest funkcja decrementWakeLock(), która zmniejsza licznik blokady wybudzania i zwalnia blokadę wybudzania, jeśli wartość licznika wynosi 0.

Scenariusz: nieoczekiwana odpowiedź RIL

W tym scenariuszu niechciane odpowiedzi RIL mają w polu flagę typu blokady wybudzania, która wskazuje, czy w przypadku odpowiedzi dostawcy należy uzyskać blokadę wybudzania. Jeśli flaga jest ustawiona, ustawiany jest wygaszacz czasu, a odpowiedź jest wysyłana przez gniazdo do części Java. Gdy minutnik wygaśnie, blokada wybudzania zostanie zwolniona. Blokada czasowa może być za długa lub za krótka w przypadku różnych niechcianych odpowiedzi RIL.

Rysunek 4. Niechciana odpowiedź RIL.

Rozwiązanie: z kodu Java do warstwy natywnej (ril.cpp) wysyłane jest potwierdzenie zamiast utrzymywania w warstwie natywnej wygasającego blokowania, gdy wysyłana jest nieoczekiwana odpowiedź.

Rysunek 5. Używanie potwierdzenia zamiast blokady wybudzania z limitem czasu.

Weryfikowanie przeprojektowanych blokad wybudzania

Sprawdź, czy wywołania RIL są identyfikowane jako synchroniczne lub asynchroniczne. Zużycie energii baterii może zależeć od sprzętu lub platformy, dlatego dostawcy powinni przeprowadzić testy wewnętrzne, aby sprawdzić, czy używanie nowej semantyki blokady wybudzania w przypadku wywołań asynchronicznych prowadzi do oszczędzania energii baterii.