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
- 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
- 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 clientecarwatchdog
(macrocarwatchdog_client_domain
). Consulte el código siguiente parasample_client.te
yfile_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
- En
checkIfAlive
, realice una verificación de estado. Una opción es publicar en el controlador de bucles de subprocesos. Si está sano, llameICarWatchdog::tellClientAlive
. Consulte el código siguiente paraSampleNativeClient.h
ySampleNativeClient.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
.
- Busque el demonio con el nombre y llame
ICarWatchdog::registerClient
. Consulte el código siguiente paramain.cpp
ySampleNativeClient.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
- 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
- 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
- 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.
- 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 ununordered_map
, que asigna el ID de propiedad a la configuración, para contener todas las configuraciones admitidas. La configuración paraVHAL_HEARTBEAT
se agrega al mapa, de modo que cuando se consultaVHAL_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; }
- 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 usarRecurrentTimer
para llamar a la acción que verifica el estado de VHAL y actualiza la propiedad del vehículoVHAL_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)); } }
- ( 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ículoVHAL_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.
- 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)
- 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.