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