/* * Copyright (C) 2019 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. */ #pragma once #include #include #include #include #include #include #include #include #include "utils.h" namespace aidl { namespace android { namespace hardware { namespace vibrator { using ::android::base::unique_fd; class HwApiBase { private: using NamesMap = std::map; class RecordInterface { public: virtual std::string toString(const NamesMap &names) = 0; virtual ~RecordInterface() {} }; template class Record : public RecordInterface { public: Record(const char *func, const T &value, const std::ios *stream) : mFunc(func), mValue(value), mStream(stream) {} std::string toString(const NamesMap &names) override; private: const char *mFunc; const T mValue; const std::ios *mStream; }; using Records = std::list>; static constexpr uint32_t RECORDS_SIZE = 32; public: HwApiBase(); void debug(int fd); protected: void saveName(const std::string &name, const std::ios *stream); template void open(const std::string &name, T *stream); bool has(const std::ios &stream); template bool get(T *value, std::istream *stream); template bool set(const T &value, std::ostream *stream); template bool poll(const T &value, std::istream *stream, const int32_t timeout = -1); template void record(const char *func, const T &value, const std::ios *stream); private: std::string mPathPrefix; NamesMap mNames; Records mRecords{RECORDS_SIZE}; std::mutex mRecordsMutex; std::mutex mIoMutex; }; #define HWAPI_RECORD(args...) HwApiBase::record(__FUNCTION__, ##args) template void HwApiBase::open(const std::string &name, T *stream) { saveName(name, stream); utils::openNoCreate(mPathPrefix + name, stream); } template bool HwApiBase::get(T *value, std::istream *stream) { ATRACE_NAME("HwApi::get"); std::scoped_lock ioLock{mIoMutex}; bool ret; stream->seekg(0); *stream >> *value; if (!(ret = !!*stream)) { ALOGE("Failed to read %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno)); } stream->clear(); HWAPI_RECORD(*value, stream); return ret; } template bool HwApiBase::set(const T &value, std::ostream *stream) { ATRACE_NAME("HwApi::set"); using utils::operator<<; std::scoped_lock ioLock{mIoMutex}; bool ret; *stream << value << std::endl; if (!(ret = !!*stream)) { ALOGE("Failed to write %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno)); stream->clear(); } HWAPI_RECORD(value, stream); return ret; } template bool HwApiBase::poll(const T &value, std::istream *stream, const int32_t timeoutMs) { ATRACE_NAME("HwApi::poll"); auto path = mPathPrefix + mNames[stream]; unique_fd fileFd{::open(path.c_str(), O_RDONLY)}; unique_fd epollFd{epoll_create(1)}; epoll_event event = { .events = EPOLLPRI | EPOLLET, }; T actual; bool ret; int epollRet; if (timeoutMs < -1) { ALOGE("Invalid polling timeout!"); return false; } if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fileFd, &event)) { ALOGE("Failed to poll %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno)); return false; } while ((ret = get(&actual, stream)) && (actual != value)) { epollRet = epoll_wait(epollFd, &event, 1, timeoutMs); if (epollRet <= 0) { ALOGE("Polling error or timeout! (%d)", epollRet); return false; } } HWAPI_RECORD(value, stream); return ret; } template void HwApiBase::record(const char *func, const T &value, const std::ios *stream) { std::lock_guard lock(mRecordsMutex); mRecords.emplace_back(std::make_unique>(func, value, stream)); mRecords.pop_front(); } template std::string HwApiBase::Record::toString(const NamesMap &names) { using utils::operator<<; std::stringstream ret; ret << mFunc << " '" << names.at(mStream) << "' = '" << mValue << "'"; return ret.str(); } class HwCalBase { public: HwCalBase(); void debug(int fd); protected: template bool getProperty(const char *key, T *value, const T defval); template bool getPersist(const char *key, T *value); private: std::string mPropertyPrefix; std::map mCalData; }; template bool HwCalBase::getProperty(const char *key, T *outval, const T defval) { ATRACE_NAME("HwCal::getProperty"); *outval = utils::getProperty(mPropertyPrefix + key, defval); return true; } template bool HwCalBase::getPersist(const char *key, T *value) { ATRACE_NAME("HwCal::getPersist"); auto it = mCalData.find(key); if (it == mCalData.end()) { ALOGE("Missing %s config!", key); return false; } std::stringstream stream{it->second}; utils::unpack(stream, value); if (!stream || !stream.eof()) { ALOGE("Invalid %s config!", key); return false; } return true; } } // namespace vibrator } // namespace hardware } // namespace android } // namespace aidl