Watchdog monitora l'integrità dei servizi del fornitore e del servizio VHAL e
termina qualsiasi processo non integro. Quando un processo non integro viene terminato, Watchdog
scarica lo stato del processo in /data/anr
come per gli altri dump di tipo "L'applicazione non risponde"
(ANR). In questo modo, la procedura di debug è più semplice.
Monitoraggio dell'integrità dei servizi del fornitore
I servizi del fornitore vengono monitorati sia sul lato nativo che su quello Java. Affinché un servizio fornitore venga monitorato, deve registrare una procedura di controllo di integrità con Watchdog specificando un timeout predefinito. Watchdog monitora l'integrità di un processo di controllo di integrità registrato eseguendo il ping a un intervallo relativo al timeout specificato durante la registrazione. Quando un processo pingato non risponde entro il timeout, viene considerato non integro.
Monitoraggio nativo dell'integrità dei servizi
Specifica il makefile AIDL di Watchdog
- 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 una policy SELinux
- Per aggiungere un criterio SELinux, consenti al dominio del servizio del fornitore di utilizzare Binder
(macro
binder_use
) e aggiungi il dominio del servizio del fornitore al dominio clientcarwatchdog
(macrocarwatchdog_client_domain
). Vedi il codice riportato di seguito 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)
file_contexts
/vendor/bin/sample_native_client u:object_r:sample_client_exec:s0
Implementa una classe client ereditando BnCarWatchdogClient
- In
checkIfAlive
, esegui un controllo di integrità. Un'opzione è pubblicare nel gestore del ciclo del thread. Se è integro, chiamaICarWatchdog::tellClientAlive
. Vedi il codice riportato di seguito 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 raccoglitore e registra il cliente
Il nome dell'interfaccia del daemon watchdog dell'auto è
android.automotive.watchdog.ICarWatchdog/default
.
- Cerca il daemon con il nome e chiama
ICarWatchdog::registerClient
. Vedi il codice riportato di seguito 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); }
Monitoraggio dell'integrità dei servizi Java
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); }
Monitoraggio dell'integrità di VHAL
A differenza del monitoraggio dell'integrità del servizio del fornitore, Watchdog monitora l'integrità del servizio VHAL
sottoscrivendo la proprietà del veicolo VHAL_HEARTBEAT
.
Watchdog prevede che il valore di questa proprietà venga aggiornato una volta ogni N secondi.
Quando l'heartbeat non viene aggiornato entro questo timeout, Watchdog termina il servizio VHAL.
Nota:Watchdog monitora l'integrità del servizio VHAL solo quando
la proprietà del veicolo VHAL_HEARTBEAT
è supportata dal servizio VHAL.
L'implementazione interna di VHAL può variare in base al fornitore. Utilizza i seguenti esempi di codice come riferimenti.
- Registra la proprietà del veicolo
VHAL_HEARTBEAT
.Quando avvii il servizio VHAL, registra la proprietà del veicolo
VHAL_HEARTBEAT
. Nell'esempio riportato di seguito, viene utilizzato ununordered_map
, che mappa l'ID proprietà alla configurazione, per contenere tutte le configurazioni supportate. La configurazione perVHAL_HEARTBEAT
viene aggiunta alla mappa, in modo che quando viene eseguita una query suVHAL_HEARTBEAT
, venga restituita la configurazione corrispondente.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; }
- Aggiorna la proprietà del veicolo
VHAL_HEARTBEAT
.In base alla frequenza del controllo di integrità VHAL (spiegata in Definisci la frequenza del controllo di integrità VHAL"), aggiorna la proprietà del veicolo
VHAL_HEARTBEAT
una volta ogni N secondi. Un modo per farlo è utilizzareRecurrentTimer
per chiamare l'azione che controlla l'integrità di VHAL e aggiorna la proprietàVHAL_HEARTBEAT
del veicolo entro il timeout.Di seguito è riportata un'implementazione di esempio che utilizza
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)); } }
- (Facoltativo) Definisci la frequenza del controllo di integrità di VHAL.
La proprietà di prodotto di sola lettura
ro.carwatchdog.vhal_healthcheck.interval
di Watchdog definisce la frequenza del controllo di integrità VHAL. La frequenza predefinita del controllo di integrità (quando questa proprietà non è definita) è di tre secondi. Se tre secondi non sono sufficienti per l'aggiornamento della proprietà del veicoloVHAL_HEARTBEAT
da parte del servizio VHAL, definisci la frequenza del controllo di integrità VHAL in base alla reattività del servizio.
Eseguire il debug dei processi non integri terminati da Watchdog
Watchdog esegue il dump dello stato del processo e termina i processi non integri. Quando termina
un processo non integro, Watchdog registra il testo carwatchdog terminated
<process name> (pid:<process id>)
in logcat. Questa riga di log
fornisce informazioni sul processo terminato, come il nome e l'ID processo.
- È possibile cercare il testo menzionato in precedenza nel logcat eseguendo:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Ad esempio, quando l'app KitchenSink è un client Watchdog registrato e non risponde ai ping di Watchdog, Watchdog registra una riga come quella riportata di seguito quando termina il processo KitchenSink registrato.
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
- Per identificare la causa principale della mancata risposta, utilizza il dump del processo archiviato in
/data/anr
proprio come faresti per i casi di errore ANR dell'attività. Per recuperare il file di dump per il processo terminato, utilizza i comandi riportati di seguito.$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
Il seguente output di esempio è specifico dell'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 -----
Il file di dump per il processo KitchenSink terminato si trova in
/data/anr/anr_2020-05-01-09-50-18-290
. Inizia l'analisi utilizzando il file di dump ANR del processo terminato.