Monitore 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 em /data/anr como acontece com outros dumps de Aplicativo que Não Responde (ANR). 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 lado 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, executando ping nele 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 da integridade do serviço nativo

Especifique o makefile Watchdog AIDL

  1. Incluir 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,
    }
    

Adicione uma política SELinux

  1. Para adicionar uma política SELinux, permita que o domínio de serviço do fornecedor use o fichário ( macro binder_use ) e adicione o domínio de 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 cliente herdando BnCarWatchdogClient

  1. Em checkIfAlive , execute uma verificação de integridade. Uma opção é postar no manipulador de loop de thread. Se estiver íntegro, 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() {}
    };
    

Cadastre 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 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 saúde 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 . 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 o funcionamento 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 acordo com o fornecedor. Use os exemplos de código a seguir como referências.

  1. Cadastre a propriedade do veículo VHAL_HEARTBEAT .

    Ao iniciar o serviço VHAL, cadastre a propriedade do veículo VHAL_HEARTBEAT . No exemplo abaixo, um unordered_map , que mapeia o ID da propriedade para config, é usado para armazenar todas as configurações suportadas. 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 funcionamento padrão (quando esta propriedade não está definida) é de três segundos. Se três 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

O Watchdog descarta o estado do processo e encerra 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. Esta linha de log fornece informações sobre o processo encerrado, como o nome e o ID do processo.

  1. O logcat pode ser pesquisado pelo texto acima 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 da mesma forma que usaria para casos de ANR de atividade. Para recuperar o arquivo dump do processo encerrado, use os comandos abaixo.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    O exemplo de saída a seguir é específico do 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.