Watchdog отслеживает состояние служб поставщика и службы VHAL и завершает любые неработоспособные процессы. При завершении неработоспособного процесса Watchdog записывает его состояние в /data/anr как и в случае с другими дампами Application Not Responding (ANR). Это упрощает процесс отладки.
Мониторинг состояния сервисов поставщика
Сервисы сторонних поставщиков отслеживаются как на стороне нативных приложений, так и на стороне Java. Для мониторинга сервиса стороннего поставщика необходимо зарегистрировать процесс проверки работоспособности в Watchdog, указав предварительно заданный тайм-аут. Watchdog отслеживает работоспособность зарегистрированного процесса проверки работоспособности, отправляя ему пинг с интервалом, пропорциональным тайм-ауту, указанному при регистрации. Если процесс, получивший пинг, не отвечает в течение тайм-аута, он считается неработоспособным.
Мониторинг состояния здоровья местных жителей
Укажите файл makefile для Watchdog AIDL.
- Включите
carwatchdog_aidl_interface-ndk_platformвshared_libs.Android.bpcc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
Добавить политику SELinux
- Чтобы добавить политику SELinux, разрешите домену службы поставщика использовать binder (макрос
binder_use) и добавьте домен службы поставщика в домен клиентаcarwatchdog(макросcarwatchdog_client_domain). См. код ниже дляsample_client.teиfile_contexts:sample_client.tetype 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
Реализуйте класс клиента, унаследовав его от класса BnCarWatchdogClient.
- В
checkIfAliveвыполните проверку работоспособности. Один из вариантов — отправить запрос обработчику цикла потока. Если работоспособность положительная, вызовитеICarWatchdog::tellClientAlive. См. код ниже дляSampleNativeClient.hиSampleNativeClient.cpp:SampleNativeClient.hclass 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.cppndk::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); }
Запустите поток привязки и зарегистрируйте клиента.
Интерфейс демона Car Watchdog называется android.automotive.watchdog.ICarWatchdog/default .
- Найдите демон с указанным именем и вызовите метод
ICarWatchdog::registerClient. См. код ниже дляmain.cppиSampleNativeClient.cpp:main.cppint 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.cppvoid 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); }
Мониторинг состояния Java-сервисов
Реализуйте клиент, унаследовав класс CarWatchdogClientCallback.
- Отредактируйте новый файл следующим образом:
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() {} };
Зарегистрируйте клиента
- Вызовите
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); }
Отменить регистрацию клиента
- Вызовите метод
CarWatchdogManager.unregisterClient()после завершения работы службы:private void finishClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); manager.unregisterClient(mClientCallback); }
мониторинг здоровья VHAL
В отличие от мониторинга состояния служб поставщика, Watchdog отслеживает состояние службы VHAL, подписываясь на свойство VHAL_HEARTBEAT . Watchdog ожидает, что значение этого свойства будет обновляться каждые N секунд. Если в течение этого времени пульс не обновляется, Watchdog завершает работу службы VHAL.
Примечание: Сторожевой таймер отслеживает состояние службы VHAL только в том случае, если свойство транспортного средства VHAL_HEARTBEAT поддерживается службой VHAL.
Внутренняя реализация VHAL может различаться в зависимости от производителя. Используйте следующие примеры кода в качестве参考.
- Зарегистрируйте свойство транспортного средства
VHAL_HEARTBEAT.При запуске службы VHAL зарегистрируйте свойство транспортного средства
VHAL_HEARTBEAT. В приведенном ниже примере используетсяunordered_map, сопоставляющий идентификатор свойства с конфигурацией, для хранения всех поддерживаемых конфигураций. Конфигурация дляVHAL_HEARTBEATдобавляется в карту, так что при запросеVHAL_HEARTBEATвозвращается соответствующая конфигурация.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; }
- Обновите свойство транспортного средства
VHAL_HEARTBEAT.В зависимости от частоты проверки работоспособности VHAL (подробнее см. в разделе «Определение частоты проверки работоспособности VHAL» ), обновляйте свойство транспортного средства
VHAL_HEARTBEATкаждые N секунд. Один из способов сделать это — использоватьRecurrentTimerдля вызова действия, которое проверяет работоспособность VHAL и обновляет свойство транспортного средстваVHAL_HEARTBEATв течение заданного таймаута.Ниже представлен пример реализации с использованием
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)); } }
- ( Необязательно ) Укажите частоту проверки состояния VHAL.
Свойство `
ro.carwatchdog.vhal_healthcheck.intervalдоступное только для чтения, определяет частоту проверки работоспособности VHAL. Частота проверки работоспособности по умолчанию (если это свойство не определено) составляет три секунды. Если трех секунд недостаточно для обновления свойства автомобиляVHAL_HEARTBEATслужбой VHAL, определите частоту проверки работоспособности VHAL в зависимости от скорости отклика службы.
Отладка неисправных процессов завершена системой контроля.
Watchdog выводит состояние процесса и завершает неработоспособные процессы. При завершении неработоспособного процесса Watchdog записывает в logcat текст carwatchdog terminated <process name> (pid:<process id>) . Эта строка лога содержит информацию о завершенном процессе, такую как имя процесса и идентификатор процесса.
- Поиск указанного текста в logcat можно выполнить следующим образом:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Например, когда приложение KitchenSink является зарегистрированным клиентом Watchdog и перестает отвечать на запросы Watchdog, Watchdog записывает в лог строку, подобную приведенной ниже, при завершении зарегистрированного процесса KitchenSink.
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
- Для определения первопричины зависания используйте дамп процесса, хранящийся в
/data/anrтак же, как и в случаях ANR активности. Чтобы получить файл дампа для завершившегося процесса, используйте следующие команды.$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
Приведенный ниже пример выходных данных относится именно к приложению 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 -----
Файл дампа завершенного процесса KitchenSink находится по адресу
/data/anr/anr_2020-05-01-09-50-18-290. Начните анализ, используя файл дампа ANR завершенного процесса.