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