diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index 872b35b4f0..a4fd6415ed 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -15,10 +15,12 @@ cc_defaults { name: "vhal_v2_0_defaults", shared_libs: [ + "libbinder_ndk", "libhidlbase", "liblog", "libutils", "android.hardware.automotive.vehicle@2.0", + "carwatchdog_aidl_interface-ndk_platform", ], cflags: [ "-Wall", @@ -46,6 +48,7 @@ cc_library { "common/src/VehiclePropertyStore.cpp", "common/src/VehicleUtils.cpp", "common/src/VmsUtils.cpp", + "common/src/WatchdogClient.cpp", ], shared_libs: [ "libbase", diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp index 127eb98e43..32e5e703ff 100644 --- a/automotive/vehicle/2.0/default/VehicleService.cpp +++ b/automotive/vehicle/2.0/default/VehicleService.cpp @@ -20,9 +20,12 @@ #include +#include +#include #include #include #include +#include using namespace android; using namespace android::hardware; @@ -36,7 +39,7 @@ int main(int /* argc */, char* /* argv */ []) { auto service = std::make_unique(hal.get()); connector->setValuePool(hal->getValuePool()); - configureRpcThreadpool(4, true /* callerWillJoin */); + configureRpcThreadpool(4, false /* callerWillJoin */); ALOGI("Registering as service..."); status_t status = service->registerAsService(); @@ -46,8 +49,22 @@ int main(int /* argc */, char* /* argv */ []) { return 1; } + // Setup a binder thread pool to be a car watchdog client. + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + sp looper(Looper::prepare(0 /* opts */)); + std::shared_ptr watchdogClient = + ndk::SharedRefBase::make(looper, service.get()); + // The current health check is done in the main thread, so it falls short of capturing the real + // situation. Checking through HAL binder thread should be considered. + if (!watchdogClient->initialize()) { + ALOGE("Failed to initialize car watchdog client"); + return 1; + } ALOGI("Ready"); - joinRpcThreadpool(); + while (true) { + looper->pollAll(-1 /* timeoutMillis */); + } return 1; } diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/WatchdogClient.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/WatchdogClient.h new file mode 100644 index 0000000000..578606dd21 --- /dev/null +++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/WatchdogClient.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_hardware_automotive_vehicle_V2_0_WatchdogClient_H_ +#define android_hardware_automotive_vehicle_V2_0_WatchdogClient_H_ + +#include "VehicleHalManager.h" + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +class WatchdogClient : public aidl::android::automotive::watchdog::BnCarWatchdogClient { + public: + explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper, + VehicleHalManager* vhalManager); + + ndk::ScopedAStatus checkIfAlive( + int32_t sessionId, aidl::android::automotive::watchdog::TimeoutLength timeout) override; + ndk::ScopedAStatus prepareProcessTermination() override; + + bool initialize(); + + private: + class MessageHandlerImpl : public ::android::MessageHandler { + public: + explicit MessageHandlerImpl(WatchdogClient* client); + void handleMessage(const ::android::Message& message) override; + + private: + WatchdogClient* mClient; + }; + + private: + void respondToWatchdog(); + bool isClientHealthy() const; + + private: + ::android::sp<::android::Looper> mHandlerLooper; + ::android::sp mMessageHandler; + std::shared_ptr mWatchdogServer; + std::shared_ptr mTestClient; + VehicleHalManager* mVhalManager; + ::android::Mutex mMutex; + int mCurrentSessionId GUARDED_BY(mMutex); +}; + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_WatchdogClient_H_ diff --git a/automotive/vehicle/2.0/default/common/src/WatchdogClient.cpp b/automotive/vehicle/2.0/default/common/src/WatchdogClient.cpp new file mode 100644 index 0000000000..c067216668 --- /dev/null +++ b/automotive/vehicle/2.0/default/common/src/WatchdogClient.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "automotive.vehicle@2.0-watchdog" + +#include + +#include +#include + +using aidl::android::automotive::watchdog::ICarWatchdog; +using aidl::android::automotive::watchdog::TimeoutLength; + +namespace { + +enum { WHAT_CHECK_ALIVE = 1 }; + +} // namespace + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +WatchdogClient::WatchdogClient(const sp& handlerLooper, VehicleHalManager* vhalManager) + : mHandlerLooper(handlerLooper), mVhalManager(vhalManager), mCurrentSessionId(-1) { + mMessageHandler = new MessageHandlerImpl(this); +} + +ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength /*timeout*/) { + mHandlerLooper->removeMessages(mMessageHandler, WHAT_CHECK_ALIVE); + { + Mutex::Autolock lock(mMutex); + mCurrentSessionId = sessionId; + } + mHandlerLooper->sendMessage(mMessageHandler, Message(WHAT_CHECK_ALIVE)); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus WatchdogClient::prepareProcessTermination() { + return ndk::ScopedAStatus::ok(); +} + +bool WatchdogClient::initialize() { + ndk::SpAIBinder binder( + AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default")); + if (binder.get() == nullptr) { + ALOGE("Failed to get carwatchdog daemon"); + return false; + } + std::shared_ptr server = ICarWatchdog::fromBinder(binder); + if (server == nullptr) { + ALOGE("Failed to connect to carwatchdog daemon"); + return false; + } + mWatchdogServer = server; + + binder = this->asBinder(); + if (binder.get() == nullptr) { + ALOGE("Failed to get car watchdog client binder object"); + return false; + } + std::shared_ptr client = ICarWatchdogClient::fromBinder(binder); + if (client == nullptr) { + ALOGE("Failed to get ICarWatchdogClient from binder"); + return false; + } + mTestClient = client; + mWatchdogServer->registerClient(client, TimeoutLength::TIMEOUT_NORMAL); + ALOGI("Successfully registered the client to car watchdog server"); + return true; +} + +void WatchdogClient::respondToWatchdog() { + if (mWatchdogServer == nullptr) { + ALOGW("Cannot respond to car watchdog daemon: car watchdog daemon is not connected"); + return; + } + int sessionId; + { + Mutex::Autolock lock(mMutex); + sessionId = mCurrentSessionId; + } + if (isClientHealthy()) { + ndk::ScopedAStatus status = mWatchdogServer->tellClientAlive(mTestClient, sessionId); + if (!status.isOk()) { + ALOGE("Failed to call tellClientAlive(session id = %d): %d", sessionId, + status.getStatus()); + return; + } + } +} + +bool WatchdogClient::isClientHealthy() const { + // We consider that default vehicle HAL is healthy if we can get PERF_VEHICLE_SPEED value. + StatusCode status = StatusCode::TRY_AGAIN; + VehiclePropValue propValue = {.prop = (int32_t)VehicleProperty::PERF_VEHICLE_SPEED}; + while (status == StatusCode::TRY_AGAIN) { + mVhalManager->get(propValue, + [&propValue, &status](StatusCode s, const VehiclePropValue& v) { + status = s; + if (s == StatusCode::OK) { + propValue = v; + } + }); + } + return status == StatusCode::OK; +} + +WatchdogClient::MessageHandlerImpl::MessageHandlerImpl(WatchdogClient* client) : mClient(client) {} + +void WatchdogClient::MessageHandlerImpl::handleMessage(const Message& message) { + switch (message.what) { + case WHAT_CHECK_ALIVE: + mClient->respondToWatchdog(); + break; + default: + ALOGW("Unknown message: %d", message.what); + } +} + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android