Supervisa el estado del sistema

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

Supervisión del estado de los servicios del proveedor

Los servicios de los proveedores se supervisan en el código nativo y Java. Para que se supervise un servicio de proveedor, el servicio debe registrar un proceso de verificación de estado con el perro guardián a través de la especificación de un tiempo de espera predefinido. El supervisor supervisa el estado de un proceso de verificación de estado registrado mediante un ping en un intervalo en relación con el tiempo de espera que se especifica durante el registro. Cuando un proceso al que se le envió un ping no responde dentro del tiempo de espera, se considera que no está en buen estado.

Supervisión del estado del servicio nativo

Especifica el makefile de AIDL de Watchdog

  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,
    }
    

Cómo agregar una política de 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)
    

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

Supervisión del estado de los servicios de Java

Cómo implementar un cliente heredando CarWatchdogClientCallback

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

Supervisión del estado de VHAL

A diferencia de la supervisión del estado del servicio 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 mensaje de estado no se actualiza dentro de este tiempo de espera, Watchdog finaliza el servicio de VHAL.

Nota: El servicio de supervisión 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 las siguientes muestras 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 para que, cuando se consulte VHAL_HEARTBEAT, se muestre 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. Se actualizó la propiedad del vehículo VHAL_HEARTBEAT.

    Según la frecuencia de la verificación de estado de VHAL (explicada 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 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 ejemplo 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 ro.carwatchdog.vhal_healthcheck.interval de solo lectura de Watchdog define la frecuencia de las verificaciones de estado de VHAL. La frecuencia predeterminada de la verificación de estado (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.

Depuración de procesos no saludables que finalizó el servicio de supervisión

El perro guardián vuelca el estado del proceso y finaliza los procesos en mal estado. Cuando se 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 cerrado, como el nombre y el ID del proceso.

  1. Para buscar en el logcat el texto mencionado anteriormente, ejecuta 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 del proceso 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 cerrado, 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 para el proceso KitchenSink finalizado 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.