Monitorear la salud del sistema

Watchdog monitorea el estado de los servicios del proveedor y el servicio VHAL, y finaliza cualquier proceso en mal estado. Cuando finaliza un proceso en mal estado, Watchdog vuelca el estado del proceso en /data/anr como ocurre con otros volcados de aplicaciones que no responden (ANR). Hacerlo facilita el proceso de depuración.

Monitoreo del estado del servicio del proveedor

Los servicios del proveedor se monitorean tanto en el lado nativo como en el de Java. Para que un servicio de Proveedor sea monitoreado, el servicio debe registrar un proceso de verificación de estado con Watchdog especificando un tiempo de espera predefinido. Watchdog monitorea el estado de un proceso de verificación de estado registrado haciéndole ping en un intervalo relativo al tiempo de espera especificado durante el registro. Cuando un proceso al que se hace ping no responde dentro del tiempo de espera, el proceso se considera en mal estado.

Monitoreo de salud del servicio nativo

Especifique el archivo MAKE AIDL de Watchdog

  1. Incluya carwatchdog_aidl_interface-ndk_platform shared_libs .

    Android.bp

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

Agregar una política SELinux

  1. Para agregar una política SELinux, permita que el dominio de servicio del proveedor use Binder (macro binder_use ) y agregue el dominio de servicio del proveedor al dominio del cliente carwatchdog (macro carwatchdog_client_domain ). Consulte el código siguiente 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
    

Implementar una clase de cliente heredando BnCarWatchdogClient

  1. En checkIfAlive , realice una verificación de estado. Una opción es publicar en el controlador de bucles de subprocesos. Si está sano, llame ICarWatchdog::tellClientAlive . Consulte el código siguiente 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);
    }
    

Inicie un hilo de carpeta y registre el cliente

El nombre de la interfaz del demonio de vigilancia del automóvil es android.automotive.watchdog.ICarWatchdog/default .

  1. Busque el demonio con el nombre y llame ICarWatchdog::registerClient . Consulte el código siguiente 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);
    }
    

Monitoreo del estado del servicio Java

Implementar un cliente heredando CarWatchdogClientCallback

  1. Edite 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() {}
    };
    

Registrar al cliente

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

Dar de baja al cliente

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

Monitoreo de salud VHAL

A diferencia del monitoreo del estado del servicio del proveedor, Watchdog monitorea el estado del servicio 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 VHAL.

Nota: Watchdog monitorea el estado del servicio VHAL solo cuando la propiedad del vehículo VHAL_HEARTBEAT es compatible con el servicio VHAL.

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

  1. Registre la propiedad del vehículo VHAL_HEARTBEAT .

    Al iniciar el servicio VHAL, registre la propiedad del vehículo VHAL_HEARTBEAT . En el siguiente ejemplo, se utiliza un unordered_map , que asigna el ID de propiedad a la configuración, para contener todas las configuraciones admitidas. La configuración para VHAL_HEARTBEAT se agrega al mapa, de modo que cuando se consulta VHAL_HEARTBEAT , se devuelve 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 (explicada en Definir la frecuencia de verificación de estado de VHAL" ), actualice la propiedad del vehículo VHAL_HEARTBEAT una vez cada N segundos. Una forma de hacerlo es usar 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 usando 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 ) Defina la frecuencia del control de estado de VHAL.

    La propiedad de 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 esta propiedad no está definida) es de tres segundos. Si tres segundos no son suficientes para que el servicio VHAL actualice la propiedad del vehículo VHAL_HEARTBEAT , defina la frecuencia de verificación del estado de VHAL según la capacidad de respuesta del servicio.

Depurar procesos en mal estado finalizados por Watchdog

Watchdog vuelca el estado del proceso y finaliza los procesos en mal estado. Al finalizar 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 del proceso y el ID del proceso.

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

    Por ejemplo, cuando la aplicación KitchenSink es un cliente Watchdog registrado y deja de responder a los pings de Watchdog, Watchdog registra una línea como la siguiente al finalizar 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, use el volcado de proceso almacenado en /data/anr tal como lo usaría para los casos de actividad ANR. Para recuperar el archivo de volcado del proceso finalizado, utilice los siguientes comandos.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    El siguiente resultado de muestra es específico de la aplicación 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 para el proceso de KitchenSink finalizado se encuentra en /data/anr/anr_2020-05-01-09-50-18-290 . Inicie su análisis utilizando el archivo de volcado ANR del proceso terminado.