Utilizza il watchdog dell'auto per facilitare il debug del VHAL. Il watchdog di Car monitora
l'integrità e termina i processi non integri. Affinché un processo sia monitorato dal watchdog dell'auto, questo deve essere registrato presso il watchdog dell'auto. Quando il watchdog dell'auto termina i processi non corretti, scrive lo stato dei processi in data/anr
come per altri dump ANR (Application Not Responding). In questo modo viene gestito il processo di debug.
Questo articolo descrive come i servizi e gli HAL del fornitore possono registrare un processo con il watchdog dell'auto.
HAL del fornitore
In genere, l'HAL del fornitore utilizza un pool di thread per hwbinder
. Tuttavia,
il client watchdog dell'auto comunica con il daemon del watchdog dell'auto tramite
binder
, che è diverso da hwbinder
. Di conseguenza,
è in uso un altro pool di thread per binder
.
Specifica l'aidl per il watchdog dell'auto nel file makefile
- Includi
carwatchdog_aidl_interface-ndk_platform
inshared_libs
:Android.bp
:cc_defaults { name: "vhal_v2_0_defaults", shared_libs: [ "libbinder_ndk", "libhidlbase", "liblog", "libutils", "android.hardware.automotive.vehicle@2.0", "carwatchdog_aidl_interface-ndk_platform", ], cflags: [ "-Wall", "-Wextra", "-Werror", ], }
Aggiungere un criterio SELinux
-
Consenti a
system_server
di uccidere l'HAL. Se non haisystem_server.te
, creane uno. Ti consigliamo vivamente di aggiungere un criterio SELinux a ogni dispositivo. -
Consenti all'HAL del fornitore di utilizzare il binder (macro
binder_use
) e aggiungi l'HAL del fornitore al dominio clientcarwatchdog
(macrocarwatchdog_client_domain
). Controlla il codice riportato di seguito persystemserver.te
evehicle_default.te
:system_server.te
# Allow system_server to kill vehicle HAL allow system_server hal_vehicle_server:process sigkill;
hal_vehicle_default.te
# Configuration for register VHAL to car watchdog carwatchdog_client_domain(hal_vehicle_default) binder_use(hal_vehicle_default)
Implementa una classe client ereditando BnCarWatchdogClient
-
In
checkIfAlive
, esegui il controllo di integrità. Ad esempio, pubblicare nel gestore del loop del thread. Se è integro, chiamaICarWatchdog::tellClientAlive
. Consulta il codice seguente perWatchogClient.h
eWatchogClient.cpp
:WatchogClient.h
class WatchdogClient : public aidl::android::automotive::watchdog::BnCarWatchdogClient { public: explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper, VehicleHalManager* vhalManager);
ndk::ScopedAStatus checkIfAlive(int32_t sessionId, aidl::android::automotive::watchdog::TimeoutLength timeout) override; ndk::ScopedAStatus prepareProcessTermination() override; };WatchogClient.cpp
ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength /*timeout*/) { // Implement or call your health check logic here return ndk::ScopedAStatus::ok(); }
Avvia il thread del binder e registra il client
- Crea un pool di thread per la comunicazione del binder. Se il fornitore HAL utilizza hwbinder per il proprio scopo, devi creare un altro pool di thread per le comunicazioni con il binder del watchdog dell'auto.
-
Cerca il daemon con il nome e chiama
ICarWatchdog::registerClient
. Il nome dell'interfaccia del daemon del watchdog dell'auto èandroid.automotive.watchdog.ICarWatchdog/default
. -
In base alla reattività del servizio, seleziona uno dei tre seguenti tipi di timeout supportati dal watchdog dell'auto e poi passa il timeout nella chiamata a
ICarWatchdog::registerClient
:- critico(3 s)
- moderate(5s)
- normale(10 s)
VehicleService.cpp
eWatchogClient.cpp
:VehicleService.cpp
int main(int /* argc */, char* /* argv */ []) { // Set up thread pool for hwbinder configureRpcThreadpool(4, false /* callerWillJoin */); ALOGI("Registering as service..."); status_t status = service->registerAsService(); if (status != OK) { ALOGE("Unable to register vehicle service (%d)", status); return 1; } // Setup a binder thread pool to be a car watchdog client. ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); sp<Looper> looper(Looper::prepare(0 /* opts */)); std::shared_ptr<WatchdogClient> watchdogClient = ndk::SharedRefBase::make<WatchdogClient>(looper, service.get()); // The current health check is done in the main thread, so it falls short of capturing the real // situation. Checking through HAL binder thread should be considered. if (!watchdogClient->initialize()) { ALOGE("Failed to initialize car watchdog client"); return 1; } ALOGI("Ready"); while (true) { looper->pollAll(-1 /* timeoutMillis */); } return 1; }
WatchogClient.cpp
bool WatchdogClient::initialize() { ndk::SpAIBinder binder(AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default")); if (binder.get() == nullptr) { ALOGE("Failed to get carwatchdog daemon"); return false; } std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder); if (server == nullptr) { ALOGE("Failed to connect to carwatchdog daemon"); return false; } mWatchdogServer = server; binder = this->asBinder(); if (binder.get() == nullptr) { ALOGE("Failed to get car watchdog client binder object"); return false; } std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder); if (client == nullptr) { ALOGE("Failed to get ICarWatchdogClient from binder"); return false; } mTestClient = client; mWatchdogServer->registerClient(client, TimeoutLength::TIMEOUT_NORMAL); ALOGI("Successfully registered the client to car watchdog server"); return true; }
Servizi dei fornitori (nativi)
Specifica il file make aidl del watchdog dell'auto
- Includi
carwatchdog_aidl_interface-ndk_platform
inshared_libs
.Android.bp
cc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
Aggiungere un criterio SELinux
- Per aggiungere un criterio SELinux, consenti al dominio di servizio del fornitore di utilizzare il binder (macro
binder_use
) e aggiungi il dominio di servizio del fornitore al dominio clientcarwatchdog
(macrocarwatchdog_client_domain
). Consulta il codice seguente persample_client.te
efile_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)
contesti_file
/vendor/bin/sample_native_client u:object_r:sample_client_exec:s0
Implementa una classe client ereditando BnCarWatchdogClient
- Esegui un controllo di integrità in
checkIfAlive
. Un'opzione è pubblicare un post nel gestore dell'istruzione for del thread. Se è integro, chiamaICarWatchdog::tellClientAlive
. Consulta il codice seguente perSampleNativeClient.h
eSampleNativeClient.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); }
Avvia un thread del binder e registra il client
Il nome dell'interfaccia del daemon watchdog dell'auto è
android.automotive.watchdog.ICarWatchdog/default
.
- Cerca il demone con il nome e chiama
ICarWatchdog::registerClient
. Consulta il codice seguente permain.cpp
eSampleNativeClient.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); }
Servizi ai fornitori (Android)
Implementa un client ereditando CarWatchdogClientCallback
- Modifica il nuovo file come segue:
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() {} };
Registra il client
- Chiama
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); }
Annulla la registrazione del client
- Chiama
CarWatchdogManager.unregisterClient()
al termine del servizio:private void finishClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); manager.unregisterClient(mClientCallback); }
Rileva i processi terminati dal watchdog dell'auto
Il watchdog dell'auto esegue il dump/l'interruzione delle procedure (HAL del fornitore, servizi nativi del fornitore,
servizi Android del fornitore) registrate al watchdog dell'auto quando sono bloccate e non rispondono. Questo tipo di dumping viene rilevato controllando i logcat. Il watchdog della vettura genera un log carwatchdog killed process_name (pid:process_id)
quando un processo problematico viene dumpato o terminato. Pertanto:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Vengono acquisiti i log pertinenti. Ad esempio, se l'app KitchenSink (un client di monitoraggio delle auto) si blocca, nel log viene scritta una riga come quella riportata di seguito:
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
Per determinare il motivo o il punto in cui l'app KitchenSink si è bloccata, utilizza il dump del processo memorizzato in /data/anr
come faresti con le segnalazioni ANR di attività.
$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
Il seguente output di esempio è specifico per l'app 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 -----
Individua il file dump (ad esempio /data/anr/anr_2020-05-01-09-50-18-290
nell'esempio precedente) e avvia l'analisi.