Usługa Watchdog monitoruje stan usług dostawcy i usługi VHAL oraz zamyka wszystkie nieprawidłowo działające procesy. Gdy nieprawidłowy proces zostanie zakończony, Watchdog zapisze stan procesu w /data/anr
, tak jak w przypadku innych zrzutów typu „Aplikacja nie odpowiada” (ANR). Ułatwi to proces debugowania.
Monitorowanie stanu usług dostawców
Usługi dostawców są monitorowane zarówno po stronie natywnej, jak i po stronie Javy. Aby usługa dostawcy była monitorowana, musi zarejestrować proces sprawdzania stanu w usłudze Watchdog, określając wstępnie zdefiniowany limit czasu. Usługa Watchdog monitoruje stan zarejestrowanego procesu sprawdzania stanu, wysyłając do niego pingi w interwale powiązanym z limitem czasu określonym podczas rejestracji. Jeśli proces, do którego wysłano ping, nie odpowie przed upływem czasu oczekiwania, zostanie uznany za nieprawidłowy.
Natywne monitorowanie stanu usługi
Określanie pliku makefile interfejsu AIDL watchdoga
- Uwzględnij
carwatchdog_aidl_interface-ndk_platform
wshared_libs
.Android.bp
cc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
Dodawanie zasad SELinux
- Aby dodać zasadę SELinux, zezwól domenie usługi dostawcy na używanie bindera (makro
binder_use
) i dodaj domenę usługi dostawcy do domeny klientacarwatchdog
(makrocarwatchdog_client_domain
). Poniżej znajdziesz kod dlasample_client.te
ifile_contexts
:sample_client.te
type sample_client, domain; type sample_client_exec, exec_type, file_type, vendor_file_type; carwatchdog_client_domain(sample_client) init_daemon_domain(sample_client) binder_use(sample_client)
file_contexts
/vendor/bin/sample_native_client u:object_r:sample_client_exec:s0
Zaimplementuj klasę klienta, dziedzicząc po klasie BnCarWatchdogClient.
- W
checkIfAlive
przeprowadź kontrolę stanu. Jedną z opcji jest wysłanie postu do modułu obsługi pętli wątków. Jeśli jesteś zdrowy, zadzwoń pod numerICarWatchdog::tellClientAlive
. Poniżej znajdziesz kod dlaSampleNativeClient.h
iSampleNativeClient.cpp
:SampleNativeClient.h
class SampleNativeClient : public BnCarWatchdogClient { public: ndk::ScopedAStatus checkIfAlive(int32_t sessionId, TimeoutLength timeout) override; ndk::ScopedAStatus prepareProcessTermination() override; void initialize(); private: void respondToDaemon(); private: ::android::sp<::android::Looper> mHandlerLooper; std::shared_ptr<ICarWatchdog> mWatchdogServer; std::shared_ptr<ICarWatchdogClient> mClient; int32_t mSessionId; };
SampleNativeClient.cpp
ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength timeout) { mHandlerLooper->removeMessages(mMessageHandler, WHAT_CHECK_ALIVE); mSessionId = sessionId; mHandlerLooper->sendMessage(mMessageHandler, Message(WHAT_CHECK_ALIVE)); return ndk::ScopedAStatus::ok(); } // WHAT_CHECK_ALIVE triggers respondToDaemon from thread handler void WatchdogClient::respondToDaemon() { // your health checking method here ndk::ScopedAStatus status = mWatchdogServer->tellClientAlive(mClient, mSessionId); }
Rozpocznij wątek w aplikacji Binder i zarejestruj klienta
Nazwa interfejsu demona watchdoga w samochodzie to android.automotive.watchdog.ICarWatchdog/default
.
- Wyszukaj demona o nazwie i wywołaj
ICarWatchdog::registerClient
. Poniżej znajdziesz kod dlamain.cpp
iSampleNativeClient.cpp
:main.cpp
int main(int argc, char** argv) { sp<Looper> looper(Looper::prepare(/*opts=*/0)); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); std::shared_ptr<SampleNativeClient> client = ndk::SharedRefBase::make<SampleNatvieClient>(looper); // The client is registered in initialize() client->initialize(); ... }
SampleNativeClient.cpp
void SampleNativeClient::initialize() { ndk::SpAIBinder binder(AServiceManager_getService( "android.automotive.watchdog.ICarWatchdog/default")); std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder); mWatchdogServer = server; ndk::SpAIBinder binder = this->asBinder(); std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder) mClient = client; server->registerClient(client, TimeoutLength::TIMEOUT_NORMAL); }
Monitorowanie stanu usług Java
Implementowanie klienta przez dziedziczenie CarWatchdogClientCallback
- Zmodyfikuj nowy plik w ten sposób:
private final CarWatchdogClientCallback mClientCallback = new CarWatchdogClientCallback() { @Override public boolean onCheckHealthStatus(int sessionId, int timeout) { // Your health check logic here // Returning true implies the client is healthy // If false is returned, the client should call // CarWatchdogManager.tellClientAlive after health check is // completed } @Override public void onPrepareProcessTermination() {} };
Rejestrowanie klienta
- Zadzwoń pod numer
CarWatchdogManager.registerClient()
:private void startClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); // Choose a proper executor according to your health check method ExecutorService executor = Executors.newFixedThreadPool(1); manager.registerClient(executor, mClientCallback, CarWatchdogManager.TIMEOUT_NORMAL); }
Wyrejestrowanie klienta
- Zadzwoń pod numer
CarWatchdogManager.unregisterClient()
, gdy usługa zostanie wykonana:private void finishClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); manager.unregisterClient(mClientCallback); }
Monitorowanie stanu VHAL
W przeciwieństwie do monitorowania stanu usług dostawcy Watchdog monitoruje stan usługi VHAL, subskrybując VHAL_HEARTBEAT
właściwość pojazdu.
Usługa Watchdog oczekuje, że wartość tej właściwości będzie aktualizowana co N sekund.
Jeśli w tym czasie nie zostanie zaktualizowany sygnał, Watchdog zakończy działanie usługi VHAL.
Uwaga: usługa Watchdog monitoruje stan usługi VHAL tylko wtedy, gdy usługa VHAL obsługuje VHAL_HEARTBEAT
właściwość pojazdu.
Wewnętrzna implementacja VHAL może się różnić w zależności od dostawcy. Skorzystaj z tych przykładowych kodów.
- Zarejestruj właściwość pojazdu
VHAL_HEARTBEAT
.Podczas uruchamiania usługi VHAL zarejestruj
VHAL_HEARTBEAT
właściwość pojazdu. W przykładzie poniżej użytounordered_map
, które mapuje identyfikator usługi na konfigurację, do przechowywania wszystkich obsługiwanych konfiguracji. Konfiguracja dlaVHAL_HEARTBEAT
jest dodawana do mapy, dzięki czemu po wysłaniu zapytania oVHAL_HEARTBEAT
zwracana jest odpowiednia konfiguracja.void registerVhalHeartbeatProperty() { const VehiclePropConfig config = { .prop = toInt(VehicleProperty::VHAL_HEARTBEAT), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, }; // mConfigsById is declared as std::unordered_map<int32_t, VehiclePropConfig>. mConfigsById[config.prop] = config; }
- Zaktualizuj właściwość pojazdu
VHAL_HEARTBEAT
.Na podstawie częstotliwości sprawdzania stanu VHAL (wyjaśnionej w sekcji Określanie częstotliwości sprawdzania stanu VHAL) aktualizuj
VHAL_HEARTBEAT
właściwość pojazdu co N sekund. Możesz to zrobić, używającRecurrentTimer
, aby wywołać działanie, które sprawdza stan VHAL i aktualizuje właściwośćVHAL_HEARTBEAT
pojazdu w ramach limitu czasu.Poniżej znajdziesz przykładową implementację z użyciem
RecurrentTimer
:int main(int argc, char** argv) { RecurrentTimer recurrentTimer(updateVhalHeartbeat); recurrentTimer.registerRecurrentEvent(kHeartBeatIntervalNs, static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)); … Run service … recurrentTimer.unregisterRecurrentEvent( static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)); } void updateVhalHeartbeat(const std::vector<int32_t>& cookies) { for (int32_t property : cookies) { if (property != static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) { continue; } // Perform internal health checking such as retrieving a vehicle property to ensure // the service is responsive. doHealthCheck(); // Construct the VHAL_HEARTBEAT property with system uptime. VehiclePropValuePool valuePool; VehicleHal::VehiclePropValuePtr propValuePtr = valuePool.obtainInt64(uptimeMillis()); propValuePtr->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT); propValuePtr->areaId = 0; propValuePtr->status = VehiclePropertyStatus::AVAILABLE; propValuePtr->timestamp = elapsedRealtimeNano(); // Propagate the HAL event. onHalEvent(std::move(propValuePtr)); } }
- (Opcjonalnie) Określ częstotliwość kontroli stanu VHAL.
Właściwość produktu
ro.carwatchdog.vhal_healthcheck.interval
tylko do odczytu Watchdog określa częstotliwość kontroli stanu VHAL. Domyślna częstotliwość kontroli stanu (gdy ta właściwość nie jest zdefiniowana) wynosi 3 sekundy. Jeśli 3 sekundy to za mało, aby usługa VHAL zaktualizowałaVHAL_HEARTBEAT
właściwość pojazduVHAL_HEARTBEAT
, określ częstotliwość kontroli stanu VHAL w zależności od szybkości reakcji usługi.
Debugowanie procesów w złym stanie zakończonych przez Watchdoga
Usługa Watchdog zrzuca stan procesu i zamyka nieprawidłowe procesy. Podczas zamykania nieprawidłowego procesu Watchdog rejestruje w logcat tekst carwatchdog terminated
<process name> (pid:<process id>)
. Ten wiersz logu zawiera informacje o zakończonym procesie, takie jak jego nazwa i identyfikator.
- Aby wyszukać w logcat wspomniany tekst, uruchom to polecenie:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Jeśli na przykład aplikacja KitchenSink jest zarejestrowanym klientem usługi Watchdog i przestaje odpowiadać na pingi Watchdog, usługa Watchdog rejestruje w logach wiersz podobny do poniższego, gdy kończy zarejestrowany proces KitchenSink.
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
- Aby zidentyfikować główną przyczynę braku odpowiedzi, użyj zrzutu procesu przechowywanego w
/data/anr
, tak jak w przypadku błędów ANR związanych z aktywnością. Aby pobrać plik zrzutu dla zakończonego procesu, użyj tych poleceń.$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
Poniższe przykładowe dane wyjściowe dotyczą aplikacji KitchenSink:
$ adb shell su root grep -Hn "pid 5574" /data/anr/*.
/data/anr/anr_2020-05-01-09-50-18-290:3:----- pid 5574 at 2020-05-01 09:50:18 ----- /data/anr/anr_2020-05-01-09-50-18-290:285:----- Waiting Channels: pid 5574 at 2020-05-01 09:50:18 -----
Plik zrzutu zakończonego procesu KitchenSink znajduje się w lokalizacji
/data/anr/anr_2020-05-01-09-50-18-290
. Rozpocznij analizę, korzystając z pliku zrzutu ANR zakończonego procesu.