El watchdog supervisa el estado de los servicios del proveedor y del servicio de VHAL, y finaliza cualquier proceso en mal estado. Cuando se finaliza un proceso en mal estado, el Watchdog vuelca el estado del proceso en /data/anr
, como con otros vuelcos de Application Not Responding (ANR). Esto facilita el proceso de depuración.
Supervisión del estado del servicio del proveedor
Los servicios del proveedor se supervisan tanto en el código nativo como en el de Java. Para que se supervise un servicio de Vendor, este debe registrar un proceso de verificación de estado con el Watchdog especificando un tiempo de espera predefinido. El watchdog supervisa el estado de un proceso de verificación de estado registrado enviándole un ping a un intervalo relativo al tiempo de espera que se especifica durante el registro. Cuando un proceso al que se le hace ping no responde dentro del tiempo de espera, se considera que el proceso está en mal estado.
Supervisión nativa del estado del servicio
Cómo especificar el archivo 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 del servicio del proveedor use Binder (macro
binder_use
) y agrega el dominio del servicio del proveedor al dominio del 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 del subproceso. 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 del vinculador y registra el cliente
El nombre de la interfaz del daemon de perro guardián del automóvil 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 del servicio de Java
Implementa un cliente heredando CarWatchdogClientCallback
- Edita el archivo nuevo 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
- Llama al
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
.
El watchdog espera que el valor de esta propiedad se actualice una vez cada N segundos.
Cuando no se actualiza el latido dentro de este tiempo de espera, Watchdog finaliza el servicio de VHAL.
Nota: El supervisor solo supervisa el estado del servicio de VHAL 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 referencia.
- 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 propiedad a la configuración, para contener todos los parámetros de configuración admitidos. Se agrega la configuración paraVHAL_HEARTBEAT
al mapa, de modo que, cuando se consulteVHAL_HEARTBEAT
, se devuelva 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 la verificación de estado del VHAL (explicada en Define la frecuencia de la verificación de estado del 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 la VHAL y actualiza la propiedadVHAL_HEARTBEAT
del vehículo 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 la verificación 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 la verificación de estado de VHAL según la capacidad de respuesta del servicio.
Cómo depurar procesos en mal estado que finalizó el Watchdog
Watchdog vuelca el estado del proceso y finaliza los procesos en mal estado. Cuando 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 finalizado, como el nombre y el ID del proceso.
- Para buscar el texto mencionado anteriormente en logcat, ejecuta el siguiente comando:
$ 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 que se muestra a continuación cuando finaliza el proceso de KitchenSink registrado.
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 finalizado, usa los siguientes comandos.$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
El siguiente ejemplo de resultado 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 del proceso KitchenSink finalizado se encuentra en
/data/anr/anr_2020-05-01-09-50-18-290
. Inicia el análisis con el archivo de volcado de ANR del proceso finalizado.