Monitorar a integridade do sistema

O Watchdog monitora a integridade dos serviços do fornecedor e do serviço VHAL e encerra qualquer processo não íntegro. Quando um processo não íntegro é encerrado, o Watchdog despeja o status do processo para /data/anr como em outros despejos de ANR (Application Not Responding). Isso facilita o processo de depuração.

Monitoramento da integridade do serviço do fornecedor

Os serviços do fornecedor são monitorados tanto no lado nativo quanto no Java. Para que um serviço de fornecedor seja monitorado, o serviço deve registrar um processo de verificação de integridade com o Watchdog, especificando um tempo limite predefinido. O watchdog monitora a integridade de um processo de verificação de integridade registrado fazendo ping em um intervalo relativo ao tempo limite especificado durante o registro. Quando um processo com ping não responde dentro do tempo limite, o processo é considerado não íntegro.

Monitoramento de integridade do serviço nativo

Especifique o makefile Watchdog AIDL

  1. Inclua carwatchdog_aidl_interface-ndk_platform em shared_libs .

    Android.bp

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

Adicionar uma política SELinux

  1. Para adicionar uma política SELinux, permita que o domínio do serviço do fornecedor use o binder ( macro binder_use ) e adicione o domínio do serviço do fornecedor ao domínio do cliente carwatchdog ( macro carwatchdog_client_domain ). Veja o código abaixo para sample_client.te e 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
    

Implemente uma classe de cliente herdando BnCarWatchdogClient

  1. Em checkIfAlive , execute uma verificação de integridade. Uma opção é postar no manipulador de loop de thread. Se estiver saudável, chame ICarWatchdog::tellClientAlive . Veja o código abaixo para SampleNativeClient.h e 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 um encadeamento de fichário e registre o cliente

O nome da interface do daemon do watchdog do carro é android.automotive.watchdog.ICarWatchdog/default .

  1. Procure o daemon com o nome e chame ICarWatchdog::registerClient . Veja o código abaixo para main.cpp e 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);
    }
    

Monitoramento de integridade do serviço Java

Implemente um cliente herdando CarWatchdogClientCallback

  1. Edite o novo arquivo da seguinte forma:
    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() {}
    };
    

Registre o cliente

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

Cancelar o registro do cliente

  1. Chame CarWatchdogManager.unregisterClient() quando o serviço for concluído:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

Monitoramento de integridade VHAL

Ao contrário do monitoramento da integridade do serviço do fornecedor, o Watchdog monitora a integridade do serviço VHAL assinando a propriedade do veículo VHAL_HEARTBEAT . O Watchdog espera que o valor desta propriedade seja atualizado uma vez a cada N segundos. Quando a pulsação não é atualizada dentro desse tempo limite, o Watchdog encerra o serviço VHAL.

Nota: O Watchdog monitora a integridade do serviço VHAL somente quando a propriedade do veículo VHAL_HEARTBEAT é suportada pelo serviço VHAL.

A implementação interna do VHAL pode variar de fornecedor para fornecedor. Os exemplos de código a seguir podem ser usados ​​como referências.

  1. Registre a propriedade do veículo VHAL_HEARTBEAT .

    Ao iniciar o serviço VHAL, registre a propriedade do veículo VHAL_HEARTBEAT . No exemplo abaixo, um unordered_map , que mapeia o ID da propriedade para a configuração, é usado para manter todas as configurações com suporte. A configuração para VHAL_HEARTBEAT é adicionada ao mapa, para que quando VHAL_HEARTBEAT for consultado, a configuração correspondente seja retornada.

    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. Atualize a propriedade do veículo VHAL_HEARTBEAT .

    Com base na frequência de verificação de integridade do VHAL (explicada em Definir a frequência da verificação de integridade do VHAL" ), atualize a propriedade do veículo VHAL_HEARTBEAT uma vez a cada N segundos. Uma maneira de fazer isso é usar o RecurrentTimer para chamar a ação que verifica a integridade do VHAL e atualiza a propriedade do veículo VHAL_HEARTBEAT dentro do tempo limite.

    Abaixo é mostrado um exemplo de implementação 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 a frequência da verificação de integridade do VHAL.

    A propriedade de produto somente leitura ro.carwatchdog.vhal_healthcheck.interval do Watchdog define a frequência de verificação de integridade do VHAL. A frequência de verificação de integridade padrão (quando esta propriedade não está definida) é de três (3) segundos. Se três (3) segundos não forem suficientes para o serviço VHAL atualizar a propriedade do veículo VHAL_HEARTBEAT , defina a frequência de verificação de integridade do VHAL dependendo da capacidade de resposta do serviço.

Depurar processos não íntegros encerrados pelo Watchdog

Watchdog despeja o estado do processo e encerra os processos não íntegros. Ao encerrar um processo não íntegro, o Watchdog registra o texto carwatchdog terminated <process name> (pid:<process id>) no logcat. Essa linha de log fornece informações sobre o processo encerrado, como o nome do processo e o ID do processo.

  1. O logcat pode ser pesquisado pelo texto mencionado executando:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
    

    Por exemplo, quando o aplicativo KitchenSink é um cliente Watchdog registrado e não responde aos pings do Watchdog, o Watchdog registra uma linha como a linha abaixo ao encerrar o processo KitchenSink registrado.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
    
  2. Para identificar a causa raiz da falta de resposta, use o dump do processo armazenado em /data/anr exatamente como você usaria para casos de ANR de atividade. Para recuperar o arquivo de despejo para o processo encerrado, use os comandos abaixo.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    A saída de exemplo a seguir é específica para o aplicativo 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 -----
    

    O arquivo de despejo para o processo KitchenSink encerrado está localizado em /data/anr/anr_2020-05-01-09-50-18-290 . Inicie sua análise usando o arquivo de despejo ANR do processo encerrado.

,

O Watchdog monitora a integridade dos serviços do fornecedor e do serviço VHAL e encerra qualquer processo não íntegro. Quando um processo não íntegro é encerrado, o Watchdog despeja o status do processo para /data/anr como em outros despejos de ANR (Application Not Responding). Isso facilita o processo de depuração.

Monitoramento da integridade do serviço do fornecedor

Os serviços do fornecedor são monitorados tanto no lado nativo quanto no Java. Para que um serviço de fornecedor seja monitorado, o serviço deve registrar um processo de verificação de integridade com o Watchdog, especificando um tempo limite predefinido. O watchdog monitora a integridade de um processo de verificação de integridade registrado fazendo ping em um intervalo relativo ao tempo limite especificado durante o registro. Quando um processo com ping não responde dentro do tempo limite, o processo é considerado não íntegro.

Monitoramento de integridade do serviço nativo

Especifique o makefile Watchdog AIDL

  1. Inclua carwatchdog_aidl_interface-ndk_platform em shared_libs .

    Android.bp

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

Adicionar uma política SELinux

  1. Para adicionar uma política SELinux, permita que o domínio do serviço do fornecedor use o binder ( macro binder_use ) e adicione o domínio do serviço do fornecedor ao domínio do cliente carwatchdog ( macro carwatchdog_client_domain ). Veja o código abaixo para sample_client.te e 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
    

Implemente uma classe de cliente herdando BnCarWatchdogClient

  1. Em checkIfAlive , execute uma verificação de integridade. Uma opção é postar no manipulador de loop de thread. Se estiver saudável, chame ICarWatchdog::tellClientAlive . Veja o código abaixo para SampleNativeClient.h e 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 um encadeamento de fichário e registre o cliente

O nome da interface do daemon do watchdog do carro é android.automotive.watchdog.ICarWatchdog/default .

  1. Procure o daemon com o nome e chame ICarWatchdog::registerClient . Veja o código abaixo para main.cpp e 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);
    }
    

Monitoramento de integridade do serviço Java

Implemente um cliente herdando CarWatchdogClientCallback

  1. Edite o novo arquivo da seguinte forma:
    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() {}
    };
    

Registre o cliente

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

Cancelar o registro do cliente

  1. Chame CarWatchdogManager.unregisterClient() quando o serviço for concluído:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

Monitoramento de integridade VHAL

Ao contrário do monitoramento da integridade do serviço do fornecedor, o Watchdog monitora a integridade do serviço VHAL assinando a propriedade do veículo VHAL_HEARTBEAT . O Watchdog espera que o valor desta propriedade seja atualizado uma vez a cada N segundos. Quando a pulsação não é atualizada dentro desse tempo limite, o Watchdog encerra o serviço VHAL.

Nota: O Watchdog monitora a integridade do serviço VHAL somente quando a propriedade do veículo VHAL_HEARTBEAT é suportada pelo serviço VHAL.

A implementação interna do VHAL pode variar de fornecedor para fornecedor. Os exemplos de código a seguir podem ser usados ​​como referências.

  1. Registre a propriedade do veículo VHAL_HEARTBEAT .

    Ao iniciar o serviço VHAL, registre a propriedade do veículo VHAL_HEARTBEAT . No exemplo abaixo, um unordered_map , que mapeia o ID da propriedade para a configuração, é usado para manter todas as configurações com suporte. A configuração para VHAL_HEARTBEAT é adicionada ao mapa, para que quando VHAL_HEARTBEAT for consultado, a configuração correspondente seja retornada.

    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. Atualize a propriedade do veículo VHAL_HEARTBEAT .

    Com base na frequência de verificação de integridade do VHAL (explicada em Definir a frequência da verificação de integridade do VHAL" ), atualize a propriedade do veículo VHAL_HEARTBEAT uma vez a cada N segundos. Uma maneira de fazer isso é usar o RecurrentTimer para chamar a ação que verifica a integridade do VHAL e atualiza a propriedade do veículo VHAL_HEARTBEAT dentro do tempo limite.

    Abaixo é mostrado um exemplo de implementação 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 a frequência da verificação de integridade do VHAL.

    A propriedade de produto somente leitura ro.carwatchdog.vhal_healthcheck.interval do Watchdog define a frequência de verificação de integridade do VHAL. A frequência de verificação de integridade padrão (quando esta propriedade não está definida) é de três (3) segundos. Se três (3) segundos não forem suficientes para o serviço VHAL atualizar a propriedade do veículo VHAL_HEARTBEAT , defina a frequência de verificação de integridade do VHAL dependendo da capacidade de resposta do serviço.

Depurar processos não íntegros encerrados pelo Watchdog

Watchdog despeja o estado do processo e encerra os processos não íntegros. Ao encerrar um processo não íntegro, o Watchdog registra o texto carwatchdog terminated <process name> (pid:<process id>) no logcat. Essa linha de log fornece informações sobre o processo encerrado, como o nome do processo e o ID do processo.

  1. O logcat pode ser pesquisado pelo texto mencionado executando:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
    

    Por exemplo, quando o aplicativo KitchenSink é um cliente Watchdog registrado e não responde aos pings do Watchdog, o Watchdog registra uma linha como a linha abaixo ao encerrar o processo KitchenSink registrado.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
    
  2. Para identificar a causa raiz da falta de resposta, use o dump do processo armazenado em /data/anr exatamente como você usaria para casos de ANR de atividade. Para recuperar o arquivo de despejo para o processo encerrado, use os comandos abaixo.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    A saída de exemplo a seguir é específica para o aplicativo 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 -----
    

    O arquivo de despejo para o processo KitchenSink encerrado está localizado em /data/anr/anr_2020-05-01-09-50-18-290 . Inicie sua análise usando o arquivo de despejo ANR do processo encerrado.