Perro guardián del vehículo

Usa el perro guardián del vehículo para ayudar a depurar el VHAL. El supervisor de Car supervisa el estado de los procesos en mal estado y los finaliza. Para que el perro guardián del vehículo supervise un proceso, este debe estar registrado en él. Cuando el supervisor de la batería del automóvil finaliza los procesos en mal estado, escribe el estado de los procesos en data/anr, al igual que con otros volcados de la aplicación que no responde (ANR). Esto facilita el proceso de depuración.

En este artículo, se describe cómo los servicios y los HAL de los proveedores pueden registrar un proceso con el supervisor del automóvil.

HAL del proveedor

Por lo general, el HAL del proveedor usa un grupo de subprocesos para hwbinder. Sin embargo, el cliente del perro guardián del vehículo se comunica con el daemon del perro guardián del vehículo a través de binder, que difiere de hwbinder. Por lo tanto, se está usando otro grupo de subprocesos para binder.

Especifica el aidl de perro guardián del vehículo en el archivo makefile

  1. Incluye carwatchdog_aidl_interface-ndk_platform en 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",
        ],
    }
    

Cómo agregar una política de SELinux

  1. Permite que system_server mate tu HAL. Si no tienes system_server.te, créala. Te recomendamos que agregues una política SELinux a cada dispositivo.
  2. Permite que el HAL del proveedor use Binder (macro binder_use) y agrega el HAL del proveedor al dominio de cliente carwatchdog (macro carwatchdog_client_domain). Consulta el siguiente código para systemserver.te y vehicle_default.te:

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

Hereda BnCarWatchdogClient para implementar una clase de cliente

  1. En checkIfAlive, realiza la verificación de estado. Por ejemplo, publica en el controlador de bucle de subprocesos. Si está en buen estado, llama a ICarWatchdog::tellClientAlive. Consulta el siguiente código para WatchogClient.h y 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();
    }
    

Inicia el subproceso de Binder y registra el cliente

  1. Crea un grupo de subprocesos para la comunicación de Binder. Si el HAL del proveedor usa hwbinder para su propio propósito, debes crear otro grupo de subprocesos para la comunicación del vinculador de watchdog del automóvil.
  2. Busca el daemon con el nombre y llama a ICarWatchdog::registerClient. El nombre de la interfaz del daemon de watchdog del automóvil es android.automotive.watchdog.ICarWatchdog/default.
  3. Según la capacidad de respuesta del servicio, selecciona uno de los siguientes tres tipos de tiempo de espera que admite el supervisor de vehículos y, luego, pasa el tiempo de espera en la llamada a ICarWatchdog::registerClient:
    • critical(3s)
    • moderate(5s)
    • normal(10s)
    Consulta el siguiente código para VehicleService.cpp y WatchogClient.cpp:

    VehículoService.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;
    }
    

Servicios de proveedores (nativos)

Especifica el archivo de configuración de make aidl del perro guardián del vehículo

  1. Incluye carwatchdog_aidl_interface-ndk_platform en shared_libs.

    Android.bp

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

Agrega una política SELinux

  1. Para agregar una política de SELinux, permite que el dominio de servicio del proveedor use Binder (macro binder_use) y agrégalo al dominio de cliente carwatchdog (macro carwatchdog_client_domain). Consulta el siguiente código para sample_client.te y 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)
    

    contextos_archivo

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

Implementa una clase de cliente heredando BnCarWatchdogClient

  1. En checkIfAlive, realiza una verificación de estado. Una opción es publicar en el controlador de bucle de subprocesos. Si está en buen estado, llama a ICarWatchdog::tellClientAlive. Consulta el siguiente código para SampleNativeClient.h y 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);
    }
    

Inicia un subproceso de Binder y registra el cliente

El nombre de la interfaz del daemon de perro guardián del vehículo es android.automotive.watchdog.ICarWatchdog/default.

  1. Busca el daemon con el nombre y llama a ICarWatchdog::registerClient. Consulta el siguiente código para main.cpp y 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);
    }
    

Servicios de proveedores (Android)

Hereda CarWatchdogClientCallback para implementar un cliente

  1. Edita el nuevo archivo de la siguiente manera:
    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 el cliente

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

Cancela el registro del cliente

  1. Llama a CarWatchdogManager.unregisterClient() cuando finalice el servicio:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

Detecta los procesos que finalizó el perro guardián del vehículo

El watchdog del automóvil borra o finaliza procesos (HAL del proveedor, servicios nativos del proveedor y servicios de Android del proveedor) que están registrados en el watchdog del automóvil cuando están bloqueados y no responden. Para detectar este tipo de volcado, se deben verificar los logcats. El supervisor del automóvil genera un registro carwatchdog killed process_name (pid:process_id) cuando se descarta o finaliza un proceso problemático. Por lo tanto:

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

Se capturan los registros relevantes. Por ejemplo, si la app de KitchenSink (un cliente de supervisión de automóviles) falla, se escribe una línea como la siguiente en el registro:

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

Para determinar por qué o dónde se detuvo la app de KitchenSink, usa el volcado de proceso almacenado en /data/anr como lo harías con los casos de ANR de Activity.

$ adb root
$ adb shell grep -Hn "pid process_pid" /data/anr/*

El siguiente resultado de muestra es específico de la app de 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 -----

Busca el archivo de volcado (por ejemplo, /data/anr/anr_2020-05-01-09-50-18-290 en el ejemplo anterior) y comienza el análisis.