Watchdog überwacht den Zustand der Anbieterdienste und des VHAL-Dienstes und
und beendet alle fehlerhaften Prozesse. Wenn ein fehlerhafter Prozess beendet wird, sendet der Watchdog den Prozessstatus wie bei anderen ANR-Dumps (App antwortet nicht) an /data/anr
. Dies erleichtert die Fehlerbehebung.
Monitoring des Anbieterdienststatus
Die Dienste der Anbieter werden sowohl auf der nativen als auch auf der Java-Seite überwacht. Damit ein Anbieterdienst überwacht werden kann, muss der Dienst einen Systemdiagnoseprozess beim Watchdog registrieren, indem er ein vordefiniertes Zeitlimit angibt. Watchdog überwacht die Status einer registrierten Systemdiagnose durch regelmäßiges Pingen bezogen auf das Zeitlimit, das bei der Registrierung angegeben wird. Wenn ein Ping angepingt wird, nicht innerhalb des Timeouts reagiert, wird der Prozess als fehlerhaft erachtet.
Monitoring des Dienststatus
Watchdog-AIDL-Makefile angeben
- Fügen Sie
carwatchdog_aidl_interface-ndk_platform
inshared_libs
ein.Android.bp
cc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
SELinux-Richtlinie hinzufügen
- Wenn Sie eine SELinux-Richtlinie hinzufügen möchten, erlauben Sie der Domain des Anbieterdiensts, Binder zu verwenden (
binder_use
-Makro), und fügen Sie die Domain des Anbieterdiensts dercarwatchdog
-Clientdomain (carwatchdog_client_domain
-Makro) hinzu. Unten finden Sie den Code fürsample_client.te
undfile_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
Implementieren Sie eine Clientklasse, indem Sie BnCarWatchdogClient erben.
- Führen Sie in
checkIfAlive
eine Systemdiagnose durch. Eine Möglichkeit besteht darin, den Thread-Loop-Handler. Ist die Funktion fehlerfrei, rufen SieICarWatchdog::tellClientAlive
auf. Unten findest du den Code fürSampleNativeClient.h
undSampleNativeClient.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); }
Binder-Thread starten und Client registrieren
Der Schnittstellenname des Watchdog-Daemon im Auto lautet
android.automotive.watchdog.ICarWatchdog/default
- Suchen Sie nach dem Daemon mit dem Namen und rufen Sie
ICarWatchdog::registerClient
auf. Unten findest du den Code fürmain.cpp
undSampleNativeClient.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); }
Monitoring des Java-Dienststatus
Implementieren Sie einen Client, indem Sie CarWatchdogClientCallback erben.
- Bearbeiten Sie die neue Datei wie folgt:
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() {} };
Client registrieren
CarWatchdogManager.registerClient()
anrufen: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); }
Client abmelden
- Rufen Sie
CarWatchdogManager.unregisterClient()
auf, wenn der Dienst beendet ist:private void finishClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); manager.unregisterClient(mClientCallback); }
VHAL-Statusüberwachung
Im Gegensatz zur Überwachung des Dienststatus des Anbieters überwacht Watchdog den Status des VHAL-Dienstes, indem er die Fahrzeugeigenschaft VHAL_HEARTBEAT
abonniert.
Watchdog erwartet, dass der Wert dieser Eigenschaft alle n Sekunden aktualisiert wird.
Wenn der Heartbeat nicht innerhalb dieses Zeitlimits aktualisiert wird, beendet Watchdog den VHAL-Dienst.
Hinweis:Watchdog überwacht den VHAL-Dienststatus nur, wenn
Die Fahrzeugeigenschaft VHAL_HEARTBEAT
wird vom VHAL-Dienst unterstützt.
Die interne VHAL-Implementierung kann je nach Anbieter variieren. Verwenden Sie die folgenden Codebeispiele als Referenz.
- Registriere die
VHAL_HEARTBEAT
-Fahrzeugeigenschaft.Registrieren Sie beim Starten des VHAL-Dienstes die Fahrzeugeigenschaft
VHAL_HEARTBEAT
. Im folgenden Beispiel wird eineunordered_map
verwendet, die die Property-ID der Konfiguration zuordnet, um alle unterstützten Konfigurationen zu speichern. Konfiguration fürVHAL_HEARTBEAT
wird hinzugefügt zu -Zuordnung, sodass bei der Abfrage vonVHAL_HEARTBEAT
die entsprechende Konfiguration zurückgegeben.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; }
VHAL_HEARTBEAT
Fahrzeugeigenschaft aktualisieren.Basierend auf der Häufigkeit der VHAL-Systemdiagnose (Erläuterung in Häufigkeit der VHAL-Systemdiagnose definieren). Die Fahrzeugeigenschaft „
VHAL_HEARTBEAT
“ wird alle n Sekunden aktualisiert. Eine Möglichkeit dazu besteht darin, mitRecurrentTimer
die Aktion aufzurufen, die den VHAL-Status prüft und das FahrzeugattributVHAL_HEARTBEAT
innerhalb des Zeitlimits aktualisiert.Hier sehen Sie eine Beispielimplementierung mit
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)); } }
- Optional: Definieren Sie die Häufigkeit der VHAL-Systemdiagnose.
Die schreibgeschützte Produkteigenschaft
ro.carwatchdog.vhal_healthcheck.interval
von Watchdog definiert die Häufigkeit der VHAL-Systemdiagnose. Standard-Systemdiagnose Häufigkeit (wenn diese Eigenschaft nicht definiert ist) beträgt drei Sekunden. Wenn drei Sekunden nicht ausreichen, damit der VHAL-Dienst die FahrzeugeigenschaftVHAL_HEARTBEAT
aktualisiert, definieren Sie die Häufigkeit der VHAL-Systemdiagnose je nach Reaktionsfähigkeit des Dienstes.
Fehlerhafte Prozesse debuggen, die vom Watchdog beendet wurden
Watchdog gibt den Prozessstatus aus und beendet fehlerhafte Prozesse. Bei Kündigung
ein fehlerhafter Prozess ist, protokolliert Watchdog den Text carwatchdog terminated
<process name> (pid:<process id>)
in logcat. Diese Protokollzeile
enthält Informationen zum beendeten Prozess, z. B. den Prozessnamen und den Prozess.
ID.
- Sie können im Logcat nach dem oben genannten Text suchen, indem Sie Folgendes ausführen:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Beispiel: Die KitchenSink-Anwendung ist ein registrierter Watchdog-Client und nicht mehr auf Watchdog-Pings reagiert, protokolliert Watchdog eine Zeile wie die folgende beim Beenden des registrierten KitchenSink-Prozesses ein.
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
- Um die Ursache der Unreaktionsfähigkeit zu ermitteln, verwenden Sie den Prozess
Dump wird unter
/data/anr
gespeichert, so wie du es auch für Aktivitäts-ANRs verwendest Cases. Verwenden Sie die folgenden Befehle, um die Dumpdatei für den beendeten Prozess abzurufen.$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
Die folgende Beispielausgabe bezieht sich auf die KitchenSink-App:
$ 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 -----
Die Dumpdatei für den beendeten KitchenSink-Prozess befindet sich unter
/data/anr/anr_2020-05-01-09-50-18-290
. Starten Sie Ihre Analyse mit die ANR-Dumpdatei des beendeten Prozesses.