Le chien de garde surveille l'état des services du fournisseur et du service VHAL, et arrête tout processus défaillant. Lorsqu'un processus non opérationnel est arrêté, le Watchdog génère un dump de l'état du processus sur /data/anr
, comme pour les autres dumps d'erreurs ANR (Application Not Responding). Cela facilite le processus de débogage.
Surveillance de l'état des services du fournisseur
Les services du fournisseur sont surveillés à la fois côté natif et côté Java. Pour qu'un service du fournisseur soit surveillé, il doit enregistrer un processus de vérification de l'état auprès du Watchdog en spécifiant un délai avant expiration prédéfini. Le chien de garde surveille l'état d'un processus de vérification d'état enregistré en lui envoyant un ping à un intervalle lié au délai avant expiration spécifié lors de l'enregistrement. Lorsqu'un processus pingué ne répond pas dans le délai imparti, il est considéré comme non opérationnel.
Surveillance de l'état des services natifs
Spécifier le fichier makefile AIDL Watchdog
- Incluez
carwatchdog_aidl_interface-ndk_platform
dansshared_libs
.Android.bp
cc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
Ajouter une règle SELinux
- Pour ajouter une règle SELinux, autorisez le domaine de service du fournisseur à utiliser le liaisonneur (macro
binder_use
) et ajoutez le domaine de service du fournisseur au domaine clientcarwatchdog
(macrocarwatchdog_client_domain
). Consultez le code ci-dessous poursample_client.te
etfile_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
Implémenter une classe client en héritant de BnCarWatchdogClient
- Dans
checkIfAlive
, effectuez une vérification de l'état. Une option consiste à publier un message dans le gestionnaire de boucle de thread. Si l'état est opérationnel, appelezICarWatchdog::tellClientAlive
. Consultez le code ci-dessous pourSampleNativeClient.h
etSampleNativeClient.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); }
Démarrer un thread de liaison et enregistrer le client
Le nom de l'interface du daemon de surveillance de la voiture est android.automotive.watchdog.ICarWatchdog/default
.
- Recherchez le daemon portant le nom et appelez
ICarWatchdog::registerClient
. Consultez le code ci-dessous pourmain.cpp
etSampleNativeClient.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); }
Surveillance de l'état des services Java
Implémenter un client en héritant de CarWatchdogClientCallback
- Modifiez le nouveau fichier comme suit :
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() {} };
Enregistrer le client
- Appeler
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); }
Désenregistrer le client
- Appelez
CarWatchdogManager.unregisterClient()
lorsque le service est terminé :private void finishClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); manager.unregisterClient(mClientCallback); }
Surveillance de l'état de VHAL
Contrairement à la surveillance de l'état du service du fournisseur, Watchdog surveille l'état du service VHAL en s'abonnant à la propriété du véhicule VHAL_HEARTBEAT
.
Le chien de garde s'attend à ce que la valeur de cette propriété soit mise à jour toutes les N secondes.
Lorsque le battement de cœur n'est pas mis à jour dans ce délai, Watchdog arrête le service VHAL.
Remarque:Watchdog surveille l'état du service VHAL uniquement lorsque la propriété de véhicule VHAL_HEARTBEAT
est compatible avec le service VHAL.
L'implémentation interne de VHAL peut varier d'un fournisseur à l'autre. Utilisez les exemples de code suivants comme références.
- Enregistrez la propriété du véhicule
VHAL_HEARTBEAT
.Lorsque vous démarrez le service VHAL, enregistrez la propriété du véhicule
VHAL_HEARTBEAT
. Dans l'exemple ci-dessous, ununordered_map
, qui mappe l'ID de propriété à la configuration, est utilisé pour contenir toutes les configurations compatibles. La configuration deVHAL_HEARTBEAT
est ajoutée à la carte afin que, lorsqueVHAL_HEARTBEAT
est interrogé, la configuration correspondante soit renvoyée.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; }
- Mettez à jour la propriété du véhicule
VHAL_HEARTBEAT
.En fonction de la fréquence de vérification de l'état de VHAL (expliquée dans la section Définir la fréquence de vérification de l'état de VHAL), mettez à jour la propriété du véhicule
VHAL_HEARTBEAT
toutes les N secondes. Pour ce faire, vous pouvez utiliserRecurrentTimer
pour appeler l'action qui vérifie l'état du VHAL et met à jour la propriété du véhiculeVHAL_HEARTBEAT
dans le délai imparti.Vous trouverez ci-dessous un exemple d'implémentation utilisant
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)); } }
- (Facultatif) Définissez la fréquence de la vérification d'état de VHAL.
La propriété de produit en lecture seule
ro.carwatchdog.vhal_healthcheck.interval
du chien de garde définit la fréquence de vérification de l'état de VHAL. La fréquence de vérification de l'état par défaut (lorsque cette propriété n'est pas définie) est de trois secondes. Si trois secondes ne suffisent pas au service VHAL pour mettre à jour la propriété du véhiculeVHAL_HEARTBEAT
, définissez la fréquence de vérification de l'état de VHAL en fonction de la réactivité du service.
Déboguer les processus non opérationnels arrêtés par le Watchdog
Le chien de garde vide l'état du processus et arrête les processus non opérationnels. Lorsqu'il met fin à un processus non opérationnel, Watchdog consigne le texte carwatchdog terminated
<process name> (pid:<process id>)
dans Logcat. Cette ligne de journal fournit des informations sur le processus terminé, telles que son nom et son ID.
- Vous pouvez rechercher le texte mentionné dans le journal Logcat en exécutant la commande suivante :
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Par exemple, lorsque l'application KitchenSink est un client Watchdog enregistré et ne répond plus aux pings Watchdog, Watchdog consigne une ligne telle que la ligne ci-dessous lors de l'arrêt du processus KitchenSink.
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
- Pour identifier l'origine du problème d'absence de réponse, utilisez le dump de processus stocké dans
/data/anr
comme vous le feriez pour les cas d'erreur ANR d'activité. Pour récupérer le fichier de dump du processus arrêté, utilisez les commandes ci-dessous.$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
L'exemple de résultat suivant est spécifique à l'application 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 -----
Le fichier de vidage du processus KitchenSink se trouve dans
/data/anr/anr_2020-05-01-09-50-18-290
. Commencez votre analyse à l'aide du fichier de dump ANR du processus arrêté.