/* * 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. */ #include "GarageModeServerSideHandler.h" #include #include #include #include #include #include #include #include #include "Utils.h" #include "vhal_v2_0/VehicleUtils.h" namespace android::hardware::automotive::vehicle::V2_0::impl { using std::chrono::duration_cast; using std::chrono::steady_clock; using std::literals::chrono_literals::operator""s; class GarageModeServerSideHandlerImpl : public GarageModeServerSideHandler { public: GarageModeServerSideHandlerImpl(IVehicleServer* vehicleServer, VehiclePropValuePool* vehicleObjectPool, const std::string& powerStateMarkerFilePath) : mVehicleServer(vehicleServer), mValueObjectPool(vehicleObjectPool), mPowerStateMarkerPath(powerStateMarkerFilePath) { mThreads.emplace_back(std::bind(&GarageModeServerSideHandlerImpl::PowerStateWatcher, this)); mThreads.emplace_back( std::bind(&GarageModeServerSideHandlerImpl::HeartbeatTimeoutWatcher, this)); } ~GarageModeServerSideHandlerImpl() { mShuttingDownFlag.store(true); mHeartbeatCV.notify_all(); for (auto& thread : mThreads) { if (thread.joinable()) { thread.join(); } } } void HandleHeartbeat() override; private: void HeartbeatTimeoutWatcher(); void PowerStateWatcher(); void HandleNewPowerState(); recyclable_ptr CreateApPowerStateReq(VehicleApPowerStateReq state, int32_t param); IVehicleServer* const mVehicleServer; VehiclePropValuePool* const mValueObjectPool; // TODO(chenhaosjtuacm): use std::filesystem when toolchain >= gcc8 is available const std::string mPowerStateMarkerPath; std::atomic mSystemShuttingDownPrepareFlag{false}; std::atomic mShuttingDownFlag{false}; std::atomic mLastHeartbeatTime{}; std::vector mThreads; std::condition_variable mHeartbeatCV; std::mutex mHeartbeatMutex; }; void GarageModeServerSideHandlerImpl::HandleHeartbeat() { LOG(DEBUG) << __func__ << ": received heartbeat from the client"; mLastHeartbeatTime.store(steady_clock::now()); } void GarageModeServerSideHandlerImpl::HeartbeatTimeoutWatcher() { constexpr auto kHeartbeatTimeout = duration_cast(5s); constexpr auto kHeartbeatCheckPeriod = 1s; while (!mShuttingDownFlag.load()) { if (!mSystemShuttingDownPrepareFlag.load()) { std::unique_lock heartbeatLock(mHeartbeatMutex); mHeartbeatCV.wait(heartbeatLock, [this]() { return mSystemShuttingDownPrepareFlag.load() || mShuttingDownFlag.load(); }); // Reset mLastHeartbeatTime everytime after entering shutdown state HandleHeartbeat(); } auto timeSinceLastHeartbeat = steady_clock::now() - mLastHeartbeatTime.load(); if (timeSinceLastHeartbeat > kHeartbeatTimeout) { LOG(ERROR) << __func__ << ": heartbeat timeout!"; // TODO(chenhaosjtuacm): Shutdown AGL break; } std::this_thread::sleep_for(kHeartbeatCheckPeriod); } } void GarageModeServerSideHandlerImpl::PowerStateWatcher() { constexpr auto kFileStatusCheckPeriod = 1s; bool log_marker_file_not_exists_message_once = false; bool log_marker_file_no_access_message_once = false; auto call_once = [](bool* once_flag, auto&& func) { if (!*once_flag) { *once_flag = true; func(); } }; while (access(mPowerStateMarkerPath.c_str(), F_OK | R_OK) < 0) { if (errno == ENOENT) { call_once(&log_marker_file_not_exists_message_once, [this]() { LOG(ERROR) << __func__ << ": marker file " << mPowerStateMarkerPath << " has not been created yet."; }); } else { call_once(&log_marker_file_no_access_message_once, [this]() { LOG(ERROR) << __func__ << ": no read access to marker file " << mPowerStateMarkerPath; }); } std::this_thread::sleep_for(kFileStatusCheckPeriod); } int inotifyFd = inotify_init(); if (inotifyFd < 0) { LOG(ERROR) << __func__ << ": failed to open inotify instance: " << strerror(errno); return; } alignas(alignof(struct inotify_event)) char inotifyEventBuffer[4096] = {0}; [[maybe_unused]] struct inotify_event& inotifyEvent = *reinterpret_cast(inotifyEventBuffer); HandleNewPowerState(); while (!mShuttingDownFlag.load()) { int watchDescriptor = inotify_add_watch(inotifyFd, mPowerStateMarkerPath.c_str(), IN_MODIFY); if (watchDescriptor < 0) { LOG(ERROR) << __func__ << ": failed to watch file " << mPowerStateMarkerPath << " : " << strerror(errno); return; } if (!WaitForReadWithTimeout(inotifyFd, kFileStatusCheckPeriod)) { continue; } auto eventReadLen = read(inotifyFd, inotifyEventBuffer, sizeof(inotifyEventBuffer)); if (eventReadLen < 0) { LOG(ERROR) << __func__ << "failed to read the inotify event: " << strerror(errno); return; } if (eventReadLen < static_cast(sizeof(struct inotify_event))) { LOG(ERROR) << __func__ << ": failed to read the full event, min event size: " << sizeof(struct inotify_event) << ", read size: " << eventReadLen; return; } HandleNewPowerState(); } } void GarageModeServerSideHandlerImpl::HandleNewPowerState() { std::ifstream markerFileStream(mPowerStateMarkerPath); std::string powerStateString; markerFileStream >> powerStateString; LOG(INFO) << __func__ << ": set power state to " << powerStateString; if (powerStateString == "shutdown") { mVehicleServer->onPropertyValueFromCar( *CreateApPowerStateReq(VehicleApPowerStateReq::SHUTDOWN_PREPARE, toInt(VehicleApPowerStateShutdownParam::CAN_SLEEP)), true); mSystemShuttingDownPrepareFlag.store(true); mHeartbeatCV.notify_all(); } else if (powerStateString == "on") { if (mSystemShuttingDownPrepareFlag.load()) { mVehicleServer->onPropertyValueFromCar( *CreateApPowerStateReq(VehicleApPowerStateReq::CANCEL_SHUTDOWN, 0), true); mSystemShuttingDownPrepareFlag.store(false); } else { LOG(INFO) << __func__ << ": not in the shutdown state, nothing changed"; } } else { LOG(ERROR) << __func__ << ": unknown power state: " << powerStateString; } } recyclable_ptr GarageModeServerSideHandlerImpl::CreateApPowerStateReq( VehicleApPowerStateReq state, int32_t param) { auto req = mValueObjectPool->obtain(VehiclePropertyType::INT32_VEC, 2); req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); req->areaId = 0; req->timestamp = elapsedRealtimeNano(); req->status = VehiclePropertyStatus::AVAILABLE; req->value.int32Values[0] = toInt(state); req->value.int32Values[1] = param; return req; } std::unique_ptr makeGarageModeServerSideHandler( IVehicleServer* vehicleServer, VehiclePropValuePool* valueObjectPool, const std::string& powerStateMarkerFilePath) { return std::make_unique(vehicleServer, valueObjectPool, powerStateMarkerFilePath); } } // namespace android::hardware::automotive::vehicle::V2_0::impl