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
- Incluye
carwatchdog_aidl_interface-ndk_platform
enshared_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
- 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 clientecarwatchdog
(macrocarwatchdog_client_domain
). Consulta el siguiente código 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
Implementa una clase de cliente heredando BnCarWatchdogClient
- 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 aICarWatchdog::tellClientAlive
. Consulta el siguiente código 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); }
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
.
- Busca el daemon con el nombre y llama a
ICarWatchdog::registerClient
. Consulta el siguiente código 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); }
Supervisión del estado de los servicios de Java
Cómo implementar un cliente heredando CarWatchdogClientCallback
- 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
- 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
- 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.
- 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 ununordered_map
, que asigna el ID de la propiedad a la configuración para contener todas las configuraciones compatibles. La configuración deVHAL_HEARTBEAT
se agrega al mapa para que, cuando se consulteVHAL_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; }
- 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 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 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)); } }
- (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ículoVHAL_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.
- 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)
- 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.