Watchdog отслеживает работоспособность служб поставщика и службы VHAL и завершает любой неработоспособный процесс. Когда неработоспособный процесс завершается, Watchdog сбрасывает состояние процесса в /data/anr
как и в случае с другими дампами Application Not Responding (ANR). Это облегчает процесс отладки.
Мониторинг работоспособности услуг поставщика
Службы Vendor отслеживаются как на родной стороне, так и на стороне Java. Для отслеживания службы Vendor служба должна зарегистрировать процесс проверки работоспособности в Watchdog, указав предопределенный тайм-аут. Watchdog отслеживает работоспособность зарегистрированного процесса проверки работоспособности, отправляя ему пинг с интервалом, соответствующим тайм-ауту, указанному при регистрации. Если пингуемый процесс не отвечает в течение тайм-аута, процесс считается неработоспособным.
Мониторинг работоспособности собственного сервиса
Укажите файл сборки Watchdog AIDL
- Включите
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, }
Добавить политику SELinux
- Чтобы добавить политику SELinux, разрешите домену службы поставщика использовать binder (макрос
binder_use
) и добавьте домен службы поставщика в домен клиентаcarwatchdog
(макросcarwatchdog_client_domain
). Смотрите код ниже дляsample_client.te
иfile_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
Реализовать клиентский класс, унаследовав BnCarWatchdogClient
- В
checkIfAlive
выполните проверку работоспособности. Один из вариантов — отправить сообщение обработчику цикла потока. Если работоспособность здорова, вызовитеICarWatchdog::tellClientAlive
. Смотрите код ниже дляSampleNativeClient.h
иSampleNativeClient.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); }
Начните тему переплета и зарегистрируйте клиента
Имя интерфейса демона автомобильного сторожевого таймера — android.automotive.watchdog.ICarWatchdog/default
.
- Найдите демон с именем и вызовите
ICarWatchdog::registerClient
. Смотрите код ниже дляmain.cpp
иSampleNativeClient.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); }
Мониторинг работоспособности службы 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.
Примечание: Watchdog отслеживает работоспособность службы 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.
Свойство продукта Watchdog
ro.carwatchdog.vhal_healthcheck.interval
, доступное только для чтения, определяет частоту проверки работоспособности VHAL. Частота проверки работоспособности по умолчанию (когда это свойство не определено) составляет три секунды. Если трех секунд недостаточно для обновления службой VHAL свойства транспортного средстваVHAL_HEARTBEAT
, определите частоту проверки работоспособности VHAL в зависимости от отзывчивости службы.
Отладка нездоровых процессов, завершенных Watchdog
Watchdog выводит состояние процесса и завершает нездоровые процессы. При завершении нездорового процесса Watchdog регистрирует текст carwatchdog terminated <process name> (pid:<process id>)
в logcat. Эта строка журнала содержит информацию о завершенном процессе, такую как имя процесса и идентификатор процесса.
- Поиск вышеупомянутого текста в 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 завершенного процесса.