Surveiller la santé du système

Watchdog surveille la santé des services du fournisseur et du service VHAL, et met fin à tout processus défectueux. Lorsqu'un processus défectueux est terminé, le chien de garde vide l'état du processus dans /data/anr comme pour les autres dumps d'application ne répondant pas (ANR). Cela facilite le processus de débogage.

Surveillance de l’état des services du fournisseur

Les services des fournisseurs sont surveillés à la fois du côté natif et du côté Java. Pour qu'un service fournisseur soit surveillé, le service doit enregistrer un processus de vérification de l'état auprès du chien de garde en spécifiant un délai d'expiration prédéfini. Watchdog surveille l'état d'un processus de vérification de l'état enregistré en lui envoyant une requête ping à un intervalle relatif au délai d'expiration spécifié lors de l'enregistrement. Lorsqu’un processus ping ne répond pas dans le délai imparti, le processus est considéré comme défectueux.

Surveillance de l’état du service natif

Spécifiez le makefile AIDL du chien de garde

  1. Incluez carwatchdog_aidl_interface-ndk_platform dans shared_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 politique SELinux

  1. Pour ajouter une stratégie SELinux, autorisez le domaine de service du fournisseur à utiliser binder (macro binder_use ) et ajoutez le domaine de service du fournisseur au domaine client carwatchdog (macro carwatchdog_client_domain ). Voir le code ci-dessous pour sample_client.te et file_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émentez une classe client en héritant de BnCarWatchdogClient

  1. Dans checkIfAlive , effectuez un bilan de santé. Une option consiste à publier sur le gestionnaire de boucle de thread. S’il est en bonne santé, appelez ICarWatchdog::tellClientAlive . Voir le code ci-dessous pour SampleNativeClient.h et SampleNativeClient.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émarrez un fil de discussion et enregistrez le client

Le nom de l’interface du démon de surveillance de voiture est android.automotive.watchdog.ICarWatchdog/default .

  1. Recherchez le démon portant le nom et appelez ICarWatchdog::registerClient . Voir le code ci-dessous pour main.cpp et SampleNativeClient.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émentez un client en héritant de CarWatchdogClientCallback

  1. 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() {}
    };
    

Enregistrez le client

  1. Appelez 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ésinscrire le client

  1. 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 la santé 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 . Watchdog 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 met fin au service VHAL.

Remarque : Watchdog surveille l'état du service VHAL uniquement lorsque la propriété du véhicule VHAL_HEARTBEAT est prise en charge par le service VHAL.

La mise en œuvre interne de VHAL peut varier d’un fournisseur à l’autre. Les exemples de code suivants peuvent être utilisés comme références.

  1. Enregistrez la propriété du véhicule VHAL_HEARTBEAT .

    Lors du démarrage du service VHAL, enregistrez la propriété du véhicule VHAL_HEARTBEAT . Dans l'exemple ci-dessous, un unordered_map , qui mappe l'ID de propriété à la configuration, est utilisé pour contenir toutes les configurations prises en charge. La configuration pour VHAL_HEARTBEAT est ajoutée à la carte, de sorte que lorsque VHAL_HEARTBEAT est interrogé, la configuration correspondante est 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;
    }
    
  2. Mettre à jour la propriété du véhicule VHAL_HEARTBEAT .

    En fonction de la fréquence du contrôle de santé VHAL (expliquée dans Définir la fréquence du contrôle de santé VHAL" ), mettez à jour la propriété du véhicule VHAL_HEARTBEAT une fois toutes les N secondes. Une façon de procéder consiste à utiliser RecurrentTimer pour appeler l'action qui vérifie la santé VHAL et met à jour la propriété du véhicule VHAL_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));
           }
    }
    
  3. ( Facultatif ) Définissez la fréquence du contrôle de santé VHAL.

    La propriété de produit en lecture seule ro.carwatchdog.vhal_healthcheck.interval de Watchdog définit la fréquence du contrôle de santé 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 (3) secondes. Si trois (3) secondes ne suffisent pas au service VHAL pour mettre à jour la propriété du véhicule VHAL_HEARTBEAT , définissez la fréquence du contrôle de santé VHAL en fonction de la réactivité du service.

Déboguer les processus défectueux terminés par le chien de garde

Watchdog vide l'état du processus et met fin aux processus défectueux. Lors de la fin d'un processus défectueux, Watchdog enregistre le texte carwatchdog terminated <process name> (pid:<process id>) dans logcat. Cette ligne de journal fournit des informations sur le processus terminé, telles que le nom et l'ID du processus.

  1. Le logcat peut être recherché pour le texte susmentionné en exécutant :
    $ 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 de Watchdog, Watchdog enregistre une ligne telle que la ligne ci-dessous lors de la fin du processus KitchenSink enregistré.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
    
  2. Pour identifier la cause première de l'absence de réponse, utilisez le vidage de processus stocké dans /data/anr comme vous le feriez pour les cas d'activité ANR. Pour récupérer le fichier de vidage du processus terminé, utilisez les commandes ci-dessous.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    L'exemple de sortie 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 terminé se trouve dans /data/anr/anr_2020-05-01-09-50-18-290 . Démarrez votre analyse en utilisant le fichier de vidage ANR du processus terminé.