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_LastCallFailCause
i RIL_DataCallFailCause
, dzięki czemu kod dostawcy może uniknąć zwracania ogólnych błędów, takich jak CALL_FAIL_ERROR_UNSPECIFIED
i PDP_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()
i clearWakeLock(
w 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.
Rozwiązanie 1: modem utrzymuje blokadę wybudzania na potrzeby żądania RIL i odpowiedzi asynchronicznej.
- Wysyłane jest żądanie RIL, a modem uzyskuje blokadę wybudzania, aby je przetworzyć.
- 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.
- 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.
- Gdy odpowiedź dotrze do części Java, zostanie uzyskana blokada wybudzania i odpowiedź zostanie zwrócona elementowi wywołującemu.
- 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ń.
- Żądanie RIL jest wysyłane przez wywołanie funkcji
acquireWakeLock()
po stronie Javy. - Kod dostawcy nie musi uzyskiwać blokady wybudzania i może przetworzyć żądanie oraz szybko na nie odpowiedzieć.
- 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.
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ź.
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.