1 /*
2  * Copyright (C) 2019 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 #pragma once
17 
18 #include <android-base/unique_fd.h>
19 #include <log/log.h>
20 #include <sys/epoll.h>
21 #include <utils/Trace.h>
22 
23 #include <chrono>
24 #include <list>
25 #include <map>
26 #include <sstream>
27 #include <string>
28 #include <type_traits>
29 
30 #include "utils.h"
31 
32 namespace aidl {
33 namespace android {
34 namespace hardware {
35 namespace vibrator {
36 
37 using ::android::base::unique_fd;
38 
39 class HwApiBase {
40   private:
41     using NamesMap = std::map<const std::ios *, std::string>;
42 
43     class RecordInterface {
44       public:
45         virtual std::string toString(const NamesMap &names) = 0;
~RecordInterface()46         virtual ~RecordInterface() {}
47     };
48     template <typename T>
49     class Record : public RecordInterface {
50       public:
Record(const char * func,const T & value,const std::ios * stream)51         Record(const char *func, const T &value, const std::ios *stream)
52             : mFunc(func), mValue(value), mStream(stream),
53               mTp(std::chrono::system_clock::system_clock::now()) {}
54         std::string toString(const NamesMap &names) override;
55 
56       private:
57         const char *mFunc;
58         const T mValue;
59         const std::ios *mStream;
60         const std::chrono::system_clock::time_point mTp;
61     };
62     using Records = std::list<std::unique_ptr<RecordInterface>>;
63 
64     static constexpr uint32_t RECORDS_SIZE = 2048;
65 
66   public:
67     HwApiBase();
68     void debug(int fd);
69 
70   protected:
71     void saveName(const std::string &name, const std::ios *stream);
72     template <typename T>
73     void open(const std::string &name, T *stream);
74     template <typename T>
75     bool has(const T &stream);
76     template <typename T>
77     bool get(T *value, std::istream *stream);
78     template <typename T>
79     bool set(const T &value, std::ostream *stream);
80     template <typename T>
81     bool poll(const T &value, std::istream *stream, const int32_t timeout = -1);
82     template <typename T>
83     void record(const char *func, const T &value, const std::ios *stream);
84 
85   private:
86     std::string mPathPrefix;
87     NamesMap mNames;
88     Records mRecords{RECORDS_SIZE};
89     std::mutex mRecordsMutex;
90     std::mutex mIoMutex;
91 };
92 
93 #define HWAPI_RECORD(args...) HwApiBase::record(__FUNCTION__, ##args)
94 
95 template <typename T>
open(const std::string & name,T * stream)96 void HwApiBase::open(const std::string &name, T *stream) {
97     saveName(name, stream);
98     utils::openNoCreate(mPathPrefix + name, stream);
99 }
100 
101 template <typename T>
has(const T & stream)102 bool HwApiBase::has(const T &stream) {
103     if constexpr (std::is_same<T, std::fstream>::value || std::is_same<T, std::ofstream>::value ||
104                   std::is_same<T, std::ifstream>::value)
105         return stream.is_open() && !stream.fail();
106 
107     ALOGE("File stream is not of the correct type");
108     return false;
109 }
110 
111 template <typename T>
get(T * value,std::istream * stream)112 bool HwApiBase::get(T *value, std::istream *stream) {
113     ATRACE_NAME("HwApi::get");
114     std::scoped_lock ioLock{mIoMutex};
115     bool ret;
116     stream->seekg(0);
117     *stream >> *value;
118     if (!(ret = !!*stream)) {
119         ALOGE("Failed to read %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
120     }
121     stream->clear();
122     HWAPI_RECORD(*value, stream);
123     return ret;
124 }
125 
126 template <typename T>
set(const T & value,std::ostream * stream)127 bool HwApiBase::set(const T &value, std::ostream *stream) {
128     ATRACE_NAME("HwApi::set");
129     using utils::operator<<;
130     std::scoped_lock ioLock{mIoMutex};
131     bool ret;
132     *stream << value << std::endl;
133     if (!(ret = !!*stream)) {
134         ALOGE("Failed to write %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
135         stream->clear();
136     }
137     HWAPI_RECORD(value, stream);
138     return ret;
139 }
140 
141 template <typename T>
poll(const T & value,std::istream * stream,const int32_t timeoutMs)142 bool HwApiBase::poll(const T &value, std::istream *stream, const int32_t timeoutMs) {
143     ATRACE_NAME("HwApi::poll");
144     auto path = mPathPrefix + mNames[stream];
145     unique_fd fileFd{::open(path.c_str(), O_RDONLY)};
146     unique_fd epollFd{epoll_create(1)};
147     epoll_event event = {
148             .events = EPOLLPRI | EPOLLET,
149     };
150     T actual;
151     bool ret;
152     int epollRet;
153 
154     if (timeoutMs < -1) {
155         ALOGE("Invalid polling timeout!");
156         return false;
157     }
158 
159     if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fileFd, &event)) {
160         ALOGE("Failed to poll %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
161         return false;
162     }
163 
164     while ((ret = get(&actual, stream)) && (actual != value)) {
165         epollRet = epoll_wait(epollFd, &event, 1, timeoutMs);
166         if (epollRet <= 0) {
167             ALOGE("Polling error or timeout! (%d)", epollRet);
168             return false;
169         }
170     }
171 
172     HWAPI_RECORD(value, stream);
173     return ret;
174 }
175 
176 template <typename T>
record(const char * func,const T & value,const std::ios * stream)177 void HwApiBase::record(const char *func, const T &value, const std::ios *stream) {
178     std::lock_guard<std::mutex> lock(mRecordsMutex);
179     mRecords.emplace_back(std::make_unique<Record<T>>(func, value, stream));
180     mRecords.pop_front();
181 }
182 
183 template <typename T>
toString(const NamesMap & names)184 std::string HwApiBase::Record<T>::toString(const NamesMap &names) {
185     using utils::operator<<;
186     std::stringstream ret;
187     auto lTp = std::chrono::system_clock::to_time_t(mTp);
188     struct tm buf;
189     auto lTime = localtime_r(&lTp, &buf);
190 
191     ret << std::put_time(lTime, "%Y-%m-%d %H:%M:%S.") << std::setfill('0') << std::setw(3)
192         << (std::chrono::duration_cast<std::chrono::milliseconds>(mTp.time_since_epoch()) % 1000)
193                     .count()
194         << "    " << mFunc << " '" << names.at(mStream) << "' = '" << mValue << "'";
195     return ret.str();
196 }
197 
198 class HwCalBase {
199   public:
200     HwCalBase();
201     void debug(int fd);
202 
203   protected:
204     template <typename T>
205     bool getProperty(const char *key, T *value, const T defval);
206     template <typename T>
207     bool getPersist(const char *key, T *value);
208 
209   private:
210     std::string mPropertyPrefix;
211     std::string mCalPath;
212     std::map<std::string, std::string> mCalData;
213 };
214 
215 template <typename T>
getProperty(const char * key,T * outval,const T defval)216 bool HwCalBase::getProperty(const char *key, T *outval, const T defval) {
217     ATRACE_NAME("HwCal::getProperty");
218     *outval = utils::getProperty(mPropertyPrefix + key, defval);
219     return true;
220 }
221 
222 template <typename T>
getPersist(const char * key,T * value)223 bool HwCalBase::getPersist(const char *key, T *value) {
224     ATRACE_NAME("HwCal::getPersist");
225     auto it = mCalData.find(key);
226     if (it == mCalData.end()) {
227         ALOGE("Missing %s config!", key);
228         return false;
229     }
230     std::stringstream stream{it->second};
231     utils::unpack(stream, value);
232     if (!stream || !stream.eof()) {
233         ALOGE("Invalid %s config!", key);
234         return false;
235     }
236     return true;
237 }
238 
239 }  // namespace vibrator
240 }  // namespace hardware
241 }  // namespace android
242 }  // namespace aidl
243