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
- Incluir
carwatchdog_aidl_interface-ndk_platform
emshared_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
- 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 clientecarwatchdog
( macrocarwatchdog_client_domain
). Veja o código abaixo parasample_client.te
efile_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
- Em
checkIfAlive
, execute uma verificação de integridade. Uma opção é postar no manipulador de loop de thread. Se estiver íntegro, chameICarWatchdog::tellClientAlive
. Veja o código abaixo paraSampleNativeClient.h
eSampleNativeClient.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
.
- Procure o daemon com o nome e chame
ICarWatchdog::registerClient
. Veja o código abaixo paramain.cpp
eSampleNativeClient.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
- 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
- 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
- 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.
- 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, umunordered_map
, que mapeia o ID da propriedade para config, é usado para armazenar todas as configurações suportadas. A configuração paraVHAL_HEARTBEAT
é adicionada ao mapa, para que quandoVHAL_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; }
- 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 oRecurrentTimer
para chamar a ação que verifica a integridade do VHAL e atualiza a propriedade do veículoVHAL_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)); } }
- ( 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ículoVHAL_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.
- 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)
- 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.