1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "GarageModeServerSideHandler.h" 18 19 #include <chrono> 20 #include <condition_variable> 21 #include <fstream> 22 #include <thread> 23 24 #include <errno.h> 25 #include <sys/inotify.h> 26 27 #include <android-base/logging.h> 28 #include <utils/SystemClock.h> 29 30 #include "Utils.h" 31 #include "vhal_v2_0/VehicleUtils.h" 32 33 namespace android::hardware::automotive::vehicle::V2_0::impl { 34 35 using std::chrono::duration_cast; 36 using std::chrono::steady_clock; 37 using std::literals::chrono_literals::operator""s; 38 39 class GarageModeServerSideHandlerImpl : public GarageModeServerSideHandler { 40 public: 41 GarageModeServerSideHandlerImpl(IVehicleServer* vehicleServer, 42 VehiclePropValuePool* vehicleObjectPool, 43 const std::string& powerStateMarkerFilePath) 44 : mVehicleServer(vehicleServer), 45 mValueObjectPool(vehicleObjectPool), 46 mPowerStateMarkerPath(powerStateMarkerFilePath) { 47 mThreads.emplace_back(std::bind(&GarageModeServerSideHandlerImpl::PowerStateWatcher, this)); 48 mThreads.emplace_back( 49 std::bind(&GarageModeServerSideHandlerImpl::HeartbeatTimeoutWatcher, this)); 50 } 51 52 ~GarageModeServerSideHandlerImpl() { 53 mShuttingDownFlag.store(true); 54 mHeartbeatCV.notify_all(); 55 for (auto& thread : mThreads) { 56 if (thread.joinable()) { 57 thread.join(); 58 } 59 } 60 } 61 62 void HandleHeartbeat() override; 63 64 private: 65 void HeartbeatTimeoutWatcher(); 66 67 void PowerStateWatcher(); 68 69 void HandleNewPowerState(); 70 71 recyclable_ptr<VehiclePropValue> CreateApPowerStateReq(VehicleApPowerStateReq state, 72 int32_t param); 73 74 IVehicleServer* const mVehicleServer; 75 VehiclePropValuePool* const mValueObjectPool; 76 77 // TODO(chenhaosjtuacm): use std::filesystem when toolchain >= gcc8 is available 78 const std::string mPowerStateMarkerPath; 79 80 std::atomic<bool> mSystemShuttingDownPrepareFlag{false}; 81 std::atomic<bool> mShuttingDownFlag{false}; 82 std::atomic<steady_clock::time_point> mLastHeartbeatTime{}; 83 std::vector<std::thread> mThreads; 84 std::condition_variable mHeartbeatCV; 85 std::mutex mHeartbeatMutex; 86 }; 87 88 void GarageModeServerSideHandlerImpl::HandleHeartbeat() { 89 LOG(DEBUG) << __func__ << ": received heartbeat from the client"; 90 mLastHeartbeatTime.store(steady_clock::now()); 91 } 92 93 void GarageModeServerSideHandlerImpl::HeartbeatTimeoutWatcher() { 94 constexpr auto kHeartbeatTimeout = duration_cast<steady_clock::duration>(5s); 95 constexpr auto kHeartbeatCheckPeriod = 1s; 96 while (!mShuttingDownFlag.load()) { 97 if (!mSystemShuttingDownPrepareFlag.load()) { 98 std::unique_lock<std::mutex> heartbeatLock(mHeartbeatMutex); 99 mHeartbeatCV.wait(heartbeatLock, [this]() { 100 return mSystemShuttingDownPrepareFlag.load() || mShuttingDownFlag.load(); 101 }); 102 103 // Reset mLastHeartbeatTime everytime after entering shutdown state 104 HandleHeartbeat(); 105 } 106 auto timeSinceLastHeartbeat = steady_clock::now() - mLastHeartbeatTime.load(); 107 if (timeSinceLastHeartbeat > kHeartbeatTimeout) { 108 LOG(ERROR) << __func__ << ": heartbeat timeout!"; 109 // TODO(chenhaosjtuacm): Shutdown AGL 110 break; 111 } 112 std::this_thread::sleep_for(kHeartbeatCheckPeriod); 113 } 114 } 115 116 void GarageModeServerSideHandlerImpl::PowerStateWatcher() { 117 constexpr auto kFileStatusCheckPeriod = 1s; 118 119 bool log_marker_file_not_exists_message_once = false; 120 bool log_marker_file_no_access_message_once = false; 121 auto call_once = [](bool* once_flag, auto&& func) { 122 if (!*once_flag) { 123 *once_flag = true; 124 func(); 125 } 126 }; 127 128 while (access(mPowerStateMarkerPath.c_str(), F_OK | R_OK) < 0) { 129 if (errno == ENOENT) { 130 call_once(&log_marker_file_not_exists_message_once, [this]() { 131 LOG(ERROR) << __func__ << ": marker file " << mPowerStateMarkerPath 132 << " has not been created yet."; 133 }); 134 } else { 135 call_once(&log_marker_file_no_access_message_once, [this]() { 136 LOG(ERROR) << __func__ << ": no read access to marker file " 137 << mPowerStateMarkerPath; 138 }); 139 } 140 std::this_thread::sleep_for(kFileStatusCheckPeriod); 141 } 142 143 int inotifyFd = inotify_init(); 144 if (inotifyFd < 0) { 145 LOG(ERROR) << __func__ << ": failed to open inotify instance: " << strerror(errno); 146 return; 147 } 148 149 alignas(alignof(struct inotify_event)) char inotifyEventBuffer[4096] = {0}; 150 [[maybe_unused]] struct inotify_event& inotifyEvent = 151 *reinterpret_cast<struct inotify_event*>(inotifyEventBuffer); 152 153 HandleNewPowerState(); 154 while (!mShuttingDownFlag.load()) { 155 int watchDescriptor = 156 inotify_add_watch(inotifyFd, mPowerStateMarkerPath.c_str(), IN_MODIFY); 157 if (watchDescriptor < 0) { 158 LOG(ERROR) << __func__ << ": failed to watch file " << mPowerStateMarkerPath << " : " 159 << strerror(errno); 160 return; 161 } 162 163 if (!WaitForReadWithTimeout(inotifyFd, kFileStatusCheckPeriod)) { 164 continue; 165 } 166 167 auto eventReadLen = read(inotifyFd, inotifyEventBuffer, sizeof(inotifyEventBuffer)); 168 if (eventReadLen < 0) { 169 LOG(ERROR) << __func__ << "failed to read the inotify event: " << strerror(errno); 170 return; 171 } 172 if (eventReadLen < static_cast<ssize_t>(sizeof(struct inotify_event))) { 173 LOG(ERROR) << __func__ << ": failed to read the full event, min event size: " 174 << sizeof(struct inotify_event) << ", read size: " << eventReadLen; 175 return; 176 } 177 HandleNewPowerState(); 178 } 179 } 180 181 void GarageModeServerSideHandlerImpl::HandleNewPowerState() { 182 std::ifstream markerFileStream(mPowerStateMarkerPath); 183 std::string powerStateString; 184 185 markerFileStream >> powerStateString; 186 LOG(INFO) << __func__ << ": set power state to " << powerStateString; 187 188 if (powerStateString == "shutdown") { 189 mVehicleServer->onPropertyValueFromCar( 190 *CreateApPowerStateReq(VehicleApPowerStateReq::SHUTDOWN_PREPARE, 191 toInt(VehicleApPowerStateShutdownParam::CAN_SLEEP)), 192 true); 193 mSystemShuttingDownPrepareFlag.store(true); 194 mHeartbeatCV.notify_all(); 195 } else if (powerStateString == "on") { 196 if (mSystemShuttingDownPrepareFlag.load()) { 197 mVehicleServer->onPropertyValueFromCar( 198 *CreateApPowerStateReq(VehicleApPowerStateReq::CANCEL_SHUTDOWN, 0), true); 199 mSystemShuttingDownPrepareFlag.store(false); 200 } else { 201 LOG(INFO) << __func__ << ": not in the shutdown state, nothing changed"; 202 } 203 } else { 204 LOG(ERROR) << __func__ << ": unknown power state: " << powerStateString; 205 } 206 } 207 208 recyclable_ptr<VehiclePropValue> GarageModeServerSideHandlerImpl::CreateApPowerStateReq( 209 VehicleApPowerStateReq state, int32_t param) { 210 auto req = mValueObjectPool->obtain(VehiclePropertyType::INT32_VEC, 2); 211 req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); 212 req->areaId = 0; 213 req->timestamp = elapsedRealtimeNano(); 214 req->status = VehiclePropertyStatus::AVAILABLE; 215 req->value.int32Values[0] = toInt(state); 216 req->value.int32Values[1] = param; 217 return req; 218 } 219 220 std::unique_ptr<GarageModeServerSideHandler> makeGarageModeServerSideHandler( 221 IVehicleServer* vehicleServer, VehiclePropValuePool* valueObjectPool, 222 const std::string& powerStateMarkerFilePath) { 223 return std::make_unique<GarageModeServerSideHandlerImpl>(vehicleServer, valueObjectPool, 224 powerStateMarkerFilePath); 225 } 226 227 } // namespace android::hardware::automotive::vehicle::V2_0::impl 228