Supervisa el estado del sistema

Watchdog supervisa el estado de los servicios del proveedor y el servicio de VHAL, y finaliza cualquier proceso en mal estado. Cuando se finaliza un proceso en mal estado, Watchdog vuelca el estado del proceso en /data/anr, como con otros volcados de la aplicación que no responde (ANR). Esto facilita el proceso de depuración.

Supervisión del estado de los servicios del proveedor

Los servicios del proveedor se supervisan tanto en el lado nativo como en el de Java. Para que se supervise un servicio del proveedor, este debe registrar un proceso de verificación de estado con Watchdog especificando un tiempo de espera predefinido. Watchdog supervisa el estado de un proceso de verificación de estado registrado haciendo un ping en un intervalo relativo al tiempo de espera que se especifica durante el registro. Cuando un proceso con ping no responde dentro del tiempo de espera, se considera que está en mal estado.

Supervisión del estado de los servicios nativos

Especifica el archivo makefile de Watchdog AIDL

  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 de SELinux

  1. Para agregar una política de SELinux, permite que el dominio del servicio del proveedor use el binder (binder_use macro) y agrega el dominio del servicio del proveedor al carwatchdog dominio del cliente (carwatchdog_client_domain macro). 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)

    file_contexts

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

Supervisión del estado de los servicios de Java

Implementa un cliente heredando CarWatchdogClientCallback

  1. Edita el archivo nuevo 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. Llama 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);
    }

Supervisión del estado de VHAL

A diferencia de la supervisión del estado de los servicios del proveedor, Watchdog supervisa el estado del servicio de VHAL suscribiéndose a la propiedad del vehículo VHAL_HEARTBEAT. Watchdog espera que el valor de esta propiedad se actualice una vez cada N segundos. Cuando el latido no se actualiza dentro de este tiempo de espera, Watchdog finaliza el servicio de VHAL.

Nota: Watchdog supervisa el estado del servicio de VHAL solo cuando el servicio de VHAL admite la propiedad del vehículo VHAL_HEARTBEAT.

La implementación interna de VHAL puede variar según el proveedor. Usa los siguientes ejemplos de código como referencias.

  1. Registra la propiedad del vehículo VHAL_HEARTBEAT.

    Cuando inicies el servicio de VHAL, registra la propiedad del vehículo VHAL_HEARTBEAT. En el siguiente ejemplo, se usa un unordered_map, que asigna el ID de la propiedad a la configuración, para contener todas las configuraciones compatibles. La configuración de VHAL_HEARTBEAT se agrega al mapa, de modo que, cuando se consulta VHAL_HEARTBEAT, se muestra la configuración correspondiente.

    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. Actualiza la propiedad del vehículo VHAL_HEARTBEAT.

    Según la frecuencia de verificación de estado de VHAL (que se explica en Define la frecuencia de la verificación de estado de VHAL"), actualiza la propiedad del vehículo VHAL_HEARTBEAT una vez cada N segundos. Una forma de hacerlo es usar el RecurrentTimer para llamar a la acción que verifica el estado de VHAL y actualiza la propiedad del vehículo VHAL_HEARTBEAT dentro del tiempo de espera.

    A continuación, se muestra una implementación de muestra con 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. (Opcional) Define la frecuencia de la verificación de estado de VHAL.

    La propiedad del producto de solo lectura ro.carwatchdog.vhal_healthcheck.interval de Watchdog define la frecuencia de verificación de estado de VHAL. La frecuencia de verificación de estado predeterminada (cuando no se define esta propiedad) es de tres segundos. Si tres segundos no son suficientes para que el servicio de VHAL actualice la propiedad del vehículo VHAL_HEARTBEAT, define la frecuencia de verificación de estado de VHAL según la capacidad de respuesta del servicio.

Depura los procesos en mal estado que finalizó Watchdog

Watchdog vuelca el estado del proceso y finaliza los procesos en mal estado. Cuando finaliza un proceso en mal estado, Watchdog registra el texto carwatchdog terminated <process name> (pid:<process id>) en logcat. Esta línea de registro proporciona información sobre el proceso finalizado, como el nombre y el ID del proceso.

  1. Se puede buscar el texto mencionado anteriormente en logcat ejecutando lo siguiente:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"

    Por ejemplo, cuando la app de KitchenSink es un cliente registrado de Watchdog y deja de responder a los pings de Watchdog, Watchdog registra una línea como la siguiente cuando finaliza el proceso registrado de KitchenSink.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
  2. Para identificar la causa raíz de la falta de respuesta, usa el volcado de procesos almacenado en /data/anr de la misma manera que lo harías para los casos de ANR de actividad. Para recuperar el archivo de volcado del proceso finalizado, usa los siguientes comandos.
    $ 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 -----

    El archivo de volcado del proceso finalizado de KitchenSink se encuentra en /data/anr/anr_2020-05-01-09-50-18-290. Inicia tu análisis con el archivo de volcado de ANR del proceso finalizado.