Monitorare l'integrità del sistema

Watchdog monitora lo stato dei servizi del fornitore e del servizio VHAL e termina qualsiasi processo non integro. Quando un processo non integro viene terminato, il Watchdog scarica lo stato del processo su /data/anr come con altri dump ANR (Application Not Responding). Ciò facilita il processo di debug.

Monitoraggio dello stato del servizio del fornitore

I servizi del fornitore sono monitorati sia sul lato nativo che su quello Java. Affinché un servizio del fornitore possa essere monitorato, il servizio deve registrare un processo di controllo dello stato con il Watchdog specificando un timeout predefinito. Watchdog monitora lo stato di un processo di controllo dello stato registrato eseguendo il ping a un intervallo relativo al timeout specificato durante la registrazione. Quando un processo sottoposto a ping non risponde entro il timeout, il processo viene considerato non integro.

Monitoraggio dell'integrità del servizio nativo

Specificare il makefile AIDL Watchdog

  1. Includi carwatchdog_aidl_interface-ndk_platform in shared_libs .

    Android.bp

    cc_binary {
        name: "sample_native_client",
        srcs: [
            "src/*.cpp"
        ],
        shared_libs: [
            "carwatchdog_aidl_interface-ndk_platform",
            "libbinder_ndk",
        ],
        vendor: true,
    }
    

Aggiungi una policy SELinux

  1. Per aggiungere una policy SELinux, consentire al dominio del servizio del fornitore di utilizzare il raccoglitore (macro binder_use ) e aggiungere il dominio del servizio del fornitore al dominio del client carwatchdog (macro carwatchdog_client_domain ). Vedi il codice seguente per sample_client.te e 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
    

Implementa una classe client ereditando BnCarWatchdogClient

  1. In checkIfAlive eseguire un controllo dello stato. Un'opzione è postare sul gestore del loop del thread. Se integro, chiama ICarWatchdog::tellClientAlive . Vedere il codice seguente per SampleNativeClient.h e 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);
    }
    

Avvia un thread del raccoglitore e registra il client

Il nome dell'interfaccia del demone watchdog dell'auto è android.automotive.watchdog.ICarWatchdog/default .

  1. Cerca il demone con il nome e chiama ICarWatchdog::registerClient . Vedi il codice seguente per main.cpp e 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);
    }
    

Monitoraggio dello stato del servizio Java

Implementa un client ereditando CarWatchdogClientCallback

  1. 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 cliente

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

Annullare la registrazione del cliente

  1. Chiama CarWatchdogManager.unregisterClient() al termine del servizio:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

Monitoraggio sanitario 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 . Il 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: il 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. Utilizzare i seguenti esempi di codice come riferimenti.

  1. Registra la proprietà del veicolo VHAL_HEARTBEAT .

    Quando si avvia il servizio VHAL, registrare la proprietà del veicolo VHAL_HEARTBEAT . Nell'esempio seguente, un unordered_map , che mappa l'ID della proprietà alla configurazione, viene utilizzato per contenere tutte le configurazioni supportate. La configurazione per VHAL_HEARTBEAT viene aggiunta alla mappa, in modo che quando viene interrogata VHAL_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;
    }
    
  2. Aggiorna la proprietà del veicolo VHAL_HEARTBEAT .

    In base alla frequenza del controllo sanitario VHAL (spiegata in Definire la frequenza del controllo sanitario VHAL" ), aggiornare la proprietà del veicolo VHAL_HEARTBEAT una volta ogni N secondi. Un modo per farlo è utilizzare RecurrentTimer per chiamare l'azione che controlla la salute VHAL e aggiorna la proprietà del veicolo VHAL_HEARTBEAT entro il timeout.

    Di seguito è mostrato un esempio di implementazione 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));
           }
    }
    
  3. ( Facoltativo ) Definire la frequenza del controllo dello stato VHAL.

    La proprietà del prodotto di sola lettura ro.carwatchdog.vhal_healthcheck.interval di Watchdog definisce la frequenza del controllo dello stato VHAL. La frequenza predefinita del controllo dello stato (quando questa proprietà non è definita) è di tre secondi. Se tre secondi non sono sufficienti affinché il servizio VHAL aggiorni la proprietà del veicolo VHAL_HEARTBEAT , definire la frequenza del controllo dello stato VHAL in base alla reattività del servizio.

Eseguire il debug di processi non integri terminati dal Watchdog

Il watchdog scarica lo stato del processo e termina i processi non integri. Quando si termina un processo non integro, Watchdog registra il testo carwatchdog terminated <process name> (pid:<process id>) su logcat. Questa riga di registro fornisce informazioni sul processo terminato come il nome del processo e l'ID del processo.

  1. È possibile cercare il testo di cui sopra 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 seguente 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)
    
  2. Per identificare la causa principale della mancata risposta, utilizza il dump del processo archiviato in /data/anr proprio come utilizzeresti per i casi ANR delle attività. Per recuperare il file dump per il processo terminato utilizzare i comandi seguenti.
    $ 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 dump ANR del processo terminato.