Monitora lo stato di integrità del sistema

Il watchdog monitora l'integrità dei servizi del fornitore e del servizio VHAL e termina qualsiasi processo non integro. Quando un processo non integro viene interrotto, scarica lo stato del processo in /data/anr, come nel caso di un'altra applicazione non risponde (ANR). In questo modo, la procedura di debug è più semplice.

Monitoraggio dello stato del servizio dei fornitori

I servizi del fornitore vengono monitorati sia a livello nativo che Java. Affinché un servizio del fornitore possa essere monitorato, deve registrare una procedura di controllo di integrità con il Watchdog specificando un timeout predefinito. Watchdog monitora l'integrità di una procedura di controllo di integrità registrata inviando un ping a un intervallo relativo al timeout specificato durante la registrazione. Quando un processo sottoposto a ping non risponde entro il timeout, viene considerato non in stato di esecuzione.

Monitoraggio dell'integrità dei servizi nativi

Specifica il file make Watchdog AIDL

  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,
    }

Aggiungere un criterio SELinux

  1. 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 alla dominio cliente carwatchdog (macro carwatchdog_client_domain). Visualizza 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, esegui un controllo di integrità. Un'opzione è pubblicare su il gestore di loop dei thread. Se è integro, chiama ICarWatchdog::tellClientAlive. Consulta 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 binder e registra il client

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

  1. Cerca il daemon con il nome e chiama ICarWatchdog::registerClient. Consulta 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 dell'integrità dei servizi 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 client

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

Annulla la registrazione del client

  1. 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 l'abbonamento alla proprietà del veicolo VHAL_HEARTBEAT. Il watchdog prevede che il valore di questa proprietà venga aggiornato una volta ogni N secondi. Se il 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. Utilizza i seguenti esempi di codice come riferimenti.

  1. Registra la proprietà del veicolo VHAL_HEARTBEAT.

    Quando avvii il servizio VHAL, registra la proprietà del veicolo VHAL_HEARTBEAT. Nell'esempio seguente, un unordered_map, che mappa l'ID proprietà da configurare utilizzata per contenere tutte le configurazioni supportate. La configurazione per VHAL_HEARTBEAT viene aggiunta alla mappa, in modo che quando viene eseguita una query su 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 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 è utilizzare RecurrentTimer per chiamare l'azione che controlla l'integrità del VHAL e aggiorna la proprietà del veicolo VHAL_HEARTBEAT entro il timeout.

    Di seguito è riportato 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) Definisci la frequenza del controllo di integrità di VHAL.

    La proprietà del prodotto ro.carwatchdog.vhal_healthcheck.interval di sola lettura di Watchdog definisce la frequenza del controllo di integrità VHAL. Controllo di integrità predefinito frequenza (quando questa proprietà non è definita) è di tre secondi. Se tre secondi non sono sufficienti per il servizio VHAL per aggiornare la proprietà del veicolo VHAL_HEARTBEAT, definisci la frequenza del controllo di integrità di VHAL in base alla reattività del servizio.

Debug di processi in stato non integro terminati dal watchdog

Watchdog esegue il dump dello stato del processo e termina i processi non integri. Quando termina un processo non corretto, Watchdog registra il testo carwatchdog terminated <process name> (pid:<process id>) in logcat. Questa riga del log Fornisce informazioni sul processo terminato, ad esempio il nome del processo e il processo ID.

  1. È possibile cercare il testo sopra menzionato 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 al termine del 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 reattività, utilizza la procedura dump archiviato in /data/anr proprio come faresti per l'attività ANR d'uso diversi. Per recuperare il file di dump per il processo terminato, utilizza i comandi seguenti.
    $ 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 -----

    Il file dump per il processo KitchenSink terminato si trova all'indirizzo /data/anr/anr_2020-05-01-09-50-18-290. Inizia l'analisi utilizzando il file di dump ANR del processo terminato.