Perro guardián del vehículo

Usa el perro guardián del vehículo para depurar el VHAL. Monitores de perros guardián de automóviles el estado de los procesos en mal estado y los elimina. Para que un proceso se supervise por el perro guardián del vehículo, el proceso debe registrarse con el perro guardián del vehículo. Cuándo el perro guardián del vehículo finaliza los procesos en mal estado, el perro guardián del vehículo escribe el estado los procesos a data/anr, como con otra Aplicación no responde (ANR). Hacerlo facilita el proceso de depuración.

En este artículo, se describe cómo las HAL y los servicios del proveedor pueden registrar un proceso con el perro guardián del vehículo.

HAL del proveedor

Por lo general, la HAL del proveedor usa un conjunto de subprocesos para hwbinder. Sin embargo, el cliente de perro guardián del vehículo se comunica con el daemon de perro guardián del vehículo a través de binder, que es diferente de hwbinder. Por lo tanto, hay otro conjunto de subprocesos en uso para binder.

Especifica el ID 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",
        ],
    }
    

Agrega una política de SELinux

  1. Permite que system_server cierre tu HAL. Si no tienes system_server.te, crea una. Es muy importante se recomendó agregar una política SELinux a cada dispositivo.
  2. Permitir que la HAL del proveedor use Binder (macro binder_use) y agrega la HAL del proveedor al dominio del cliente carwatchdog. (macro carwatchdog_client_domain). Consulta el siguiente código para systemserver.te y vehicle_default.te:

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

Implementa una clase de cliente heredando BnCarWatchdogClient

  1. En checkIfAlive, realiza la verificación de estado. Por ejemplo, puedes publicar 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 conjunto de subprocesos para la comunicación de Binder. Si el proveedor HAL usa hwbinder para su para tu propio propósito, debes crear otro conjunto de subprocesos para la comunicación de Binder de perro guardián del vehículo.
  2. Busca el daemon con el nombre y llama a ICarWatchdog::registerClient. El nombre de la interfaz del daemon de perro guardián del vehículo es android.automotive.watchdog.ICarWatchdog/default
  3. En función de la capacidad de respuesta del servicio, selecciona uno de los siguientes tres tipos de tiempo de espera compatible con el perro guardián del vehículo y luego pasa el tiempo de espera en la llamada a ICarWatchdog::registerClient:
    • crítica(3 s)
    • moderado(5 s)
    • 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 makefile de la ayuda de 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 del servicio del proveedor use Binder (macro binder_use) y agrega el dominio de servicio del proveedor al Dominio de cliente carwatchdog (macro carwatchdog_client_domain). Consulta el siguiente código para sample_client.te y file_contexts:

    cliente_ejemplo.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 bucles 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)

Cómo implementar un cliente heredando CarWatchdogClientCallback

  1. Edita el archivo nuevo como se indica a continuación:
    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 procesos finalizados por el perro guardián del vehículo

Procesos de volcado y cierre de perro guardián de automóviles (HAL del proveedor, servicios nativos del proveedor, servicios de Android del proveedor) que estén registrados en el perro guardián del vehículo cuando atascado y no responde. Este volcado se detecta mediante la verificación de los logcats. El auto el perro guardián genera un registro carwatchdog killed process_name (pid:process_id) cuando un proceso problemático se vuelca o finaliza. Por lo tanto:

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

Se capturan los registros relevantes. Por ejemplo, si la app KitchenSink (un perro guardián de vehículos cliente) se bloquea, se escribe una línea como la que se muestra a continuación 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 procesos se almacena en /data/anr del mismo modo que usarías los casos de ANR de actividad.

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

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

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