Controllore dell'auto

Usa il watchdog dell'auto per eseguire il debug del VHAL. Monitor per watchdog auto l'integrità dei processi non salubri. Per monitorare un processo dal watchdog dell'auto, il processo deve essere registrato con il watchdog dell'auto. Quando il watchdog dell'auto termina i processi non integro, il watchdog dell'auto scrive lo stato i processi per data/anr come per le altre richieste di non risposta (ANR). In questo modo facilita il processo di debug.

Questo articolo descrive in che modo gli HAL e i servizi dei fornitori possono registrare un processo con il watchdog dell'auto.

HAL fornitore

In genere, l'HAL del fornitore utilizza un pool di thread per hwbinder. Tuttavia, il client watchdog dell'auto comunica con il daemon watchdog dell'auto tramite binder, che è diverso da hwbinder. Pertanto, è in uso un altro pool di thread per binder.

Specifica l'aidl per il watchdog dell'auto nel file makefile

  1. Includi carwatchdog_aidl_interface-ndk_platform in shared_libs:

    Android.bp:

    cc_defaults {
        name: "vhal_v2_0_defaults",
        shared_libs: [
            "libbinder_ndk",
            "libhidlbase",
            "liblog",
            "libutils",
            "android.hardware.automotive.vehicle@2.0",
            "carwatchdog_aidl_interface-ndk_platform",
        ],
        cflags: [
            "-Wall",
            "-Wextra",
            "-Werror",
        ],
    }
    

Aggiungi un criterio SELinux

  1. Consenti a system_server di terminare il tuo HAL. Se non hai system_server.te, creane uno. È fortemente ti consigliamo di aggiungere un criterio SELinux a ciascun dispositivo.
  2. Consenti all'HAL del fornitore di utilizzare binder (macro binder_use) e aggiungi l'HAL del fornitore al dominio client carwatchdog (macro carwatchdog_client_domain). Visualizza il codice seguente per systemserver.te e vehicle_default.te: .

    server_di_sistema.te

    # Allow system_server to kill vehicle HAL
    allow system_server hal_vehicle_server:process sigkill;
    

    hal_vehicle_default.te

    # Configuration for register VHAL to car watchdog
    carwatchdog_client_domain(hal_vehicle_default)
    binder_use(hal_vehicle_default)
    

Implementare una classe client ereditando BnCarWatchdogClient

  1. In checkIfAlive, esegui il controllo di integrità. Ad esempio, pubblica nel gestore di loop di thread. Se è integro, chiama ICarWatchdog::tellClientAlive. Visualizza il codice seguente per WatchogClient.h e WatchogClient.cpp: .

    WatchogClient.h

    class WatchdogClient : public aidl::android::automotive::watchdog::BnCarWatchdogClient {
      public:
        explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper, VehicleHalManager* vhalManager);
    
    ndk::ScopedAStatus checkIfAlive(int32_t sessionId, aidl::android::automotive::watchdog::TimeoutLength timeout) override; ndk::ScopedAStatus prepareProcessTermination() override; };

    WatchogClient.cpp

    ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength /*timeout*/) {
        // Implement or call your health check logic here
        return ndk::ScopedAStatus::ok();
    }
    

Avvia il thread di binder e registra il client

  1. Crea un pool di thread per la comunicazione con binder. Se il fornitore HAL utilizza hwbinder per i suoi del tuo scopo, devi creare un altro pool di thread per la comunicazione con il binder watchdog dell'auto.
  2. Cerca il daemon con il nome e chiama ICarWatchdog::registerClient. Il nome dell'interfaccia del daemon watchdog dell'auto è android.automotive.watchdog.ICarWatchdog/default.
  3. In base alla reattività del servizio, seleziona uno dei tre tipi di timeout seguenti supportato dal watchdog dell'auto, per poi passare il timeout nella chiamata ICarWatchdog::registerClient:
    • critico(3 s)
    • moderato(5 s)
    • normale(10 s)
    di Gemini Advanced. Visualizza il codice seguente per VehicleService.cpp e WatchogClient.cpp:

    ServizioVeicolo.cpp

    int main(int /* argc */, char* /* argv */ []) {
        // Set up thread pool for hwbinder
        configureRpcThreadpool(4, false /* callerWillJoin */);
    
        ALOGI("Registering as service...");
        status_t status = service->registerAsService();
    
        if (status != OK) {
            ALOGE("Unable to register vehicle service (%d)", status);
            return 1;
        }
    
        // Setup a binder thread pool to be a car watchdog client.
        ABinderProcess_setThreadPoolMaxThreadCount(1);
        ABinderProcess_startThreadPool();
        sp<Looper> looper(Looper::prepare(0 /* opts */));
        std::shared_ptr<WatchdogClient> watchdogClient =
                ndk::SharedRefBase::make<WatchdogClient>(looper, service.get());
        // The current health check is done in the main thread, so it falls short of capturing the real
        // situation. Checking through HAL binder thread should be considered.
        if (!watchdogClient->initialize()) {
            ALOGE("Failed to initialize car watchdog client");
            return 1;
        }
        ALOGI("Ready");
        while (true) {
            looper->pollAll(-1 /* timeoutMillis */);
        }
    
        return 1;
    }
    

    WatchogClient.cpp

    bool WatchdogClient::initialize() {
        ndk::SpAIBinder binder(AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default"));
        if (binder.get() == nullptr) {
            ALOGE("Failed to get carwatchdog daemon");
            return false;
        }
        std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder);
        if (server == nullptr) {
            ALOGE("Failed to connect to carwatchdog daemon");
            return false;
        }
        mWatchdogServer = server;
    
        binder = this->asBinder();
        if (binder.get() == nullptr) {
            ALOGE("Failed to get car watchdog client binder object");
            return false;
        }
        std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder);
        if (client == nullptr) {
            ALOGE("Failed to get ICarWatchdogClient from binder");
            return false;
        }
        mTestClient = client;
        mWatchdogServer->registerClient(client, TimeoutLength::TIMEOUT_NORMAL);
        ALOGI("Successfully registered the client to car watchdog server");
        return true;
    }
    

Servizi dei fornitori (nativi)

Specifica il makefile aidl del watchdog dell'auto

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

    contesti_file

    /vendor/bin/sample_native_client  u:object_r:sample_client_exec:s0
    

Implementare una classe client ereditando BnCarWatchdogClient

  1. Esegui un controllo di integrità in checkIfAlive. Un'opzione è pubblicare su il gestore di loop dei thread. Se è integro, chiama ICarWatchdog::tellClientAlive. Visualizza 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 di binder e registra il client

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

  1. Cerca il daemon con il nome e chiama ICarWatchdog::registerClient. Visualizza 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);
    }
    

Servizi dei fornitori (Android)

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

Rileva processi terminati dal watchdog dell'auto

Processi di dismissione/uccisione dei cani delle auto (HAL del fornitore, servizi nativi del fornitore, servizi Android forniti dal fornitore) che sono registrati nel watchdog dell'auto quando è bloccato e non risponde. Questo dumping viene rilevato controllando i logcat. L'auto il watchdog genera un log carwatchdog killed process_name (pid:process_id) quando un processo problematico viene scaricato o terminato. Quindi:

$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"

Vengono acquisiti i log pertinenti. Ad esempio, se l'app KitchenSink (un watchdog dell'auto) client) viene bloccato, una riga come quella riportata di seguito viene scritta nel log:

05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)

Per determinare perché o dove l'app KitchenSink si è bloccata, utilizza il dump del processo archiviati all'indirizzo /data/anr proprio come faresti per i casi di ANR delle attività.

$ 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 -----

Individua il file di dump (ad esempio, /data/anr/anr_2020-05-01-09-50-18-290) nell'esempio precedente) e iniziare l'analisi.