2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Jan Olszak (j.olszak@samsung.com)
22 * @brief C++ wrapper for glib input monitor
27 #include "input-monitor-config.hpp"
28 #include "input-monitor.hpp"
29 #include "zones-manager.hpp"
30 #include "exception.hpp"
32 #include "logger/logger.hpp"
33 #include "utils/exception.hpp"
34 #include "utils/fs.hpp"
35 #include "utils/fd-utils.hpp"
40 #include <linux/input.h>
42 #include <sys/types.h>
45 #include <boost/filesystem.hpp>
49 #ifdef USE_BOOST_REGEX
50 #include <boost/regex.hpp>
51 namespace rgx = boost;
57 using namespace utils;
58 namespace fs = boost::filesystem;
63 const int MAX_TIME_WINDOW_SEC = 10;
64 const int KEY_PRESSED = 1;
65 const int DEVICE_NAME_LENGTH = 256;
66 const int MAX_NUMBER_OF_EVENTS = 10;
67 const std::string DEVICE_DIR = "/dev/input/";
72 InputMonitor::InputMonitor(ipc::epoll::EventPoll& eventPoll,
73 const InputConfig& inputConfig,
74 ZonesManager* zonesManager)
75 : mConfig(inputConfig)
76 , mZonesManager(zonesManager)
78 , mEventPoll(eventPoll)
80 if (mConfig.timeWindowMs > MAX_TIME_WINDOW_SEC * 1000L) {
81 const std::string msg = "Time window exceeds maximum: " + MAX_TIME_WINDOW_SEC;
83 throw TimeoutException(msg);
86 if (mConfig.numberOfEvents > MAX_NUMBER_OF_EVENTS) {
87 const std::string msg = "Number of events exceeds maximum: " + MAX_NUMBER_OF_EVENTS;
89 throw InputMonitorException(msg);
92 mDevicePath = getDevicePath();
94 LOGT("Input monitor configuration: \n"
95 << "\tenabled: " << mConfig.enabled << "\n"
96 << "\tdevice: " << mConfig.device << "\n"
97 << "\tpath: " << mDevicePath << "\n"
98 << "\ttype: " << EV_KEY << "\n"
99 << "\tcode: " << mConfig.code << "\n"
100 << "\tvalue: " << KEY_PRESSED << "\n"
101 << "\tnumberOfEvents: " << mConfig.numberOfEvents << "\n"
102 << "\ttimeWindowMs: " << mConfig.timeWindowMs);
105 InputMonitor::~InputMonitor()
107 std::unique_lock<std::mutex> mMutex;
108 LOGD("Destroying InputMonitor");
112 void InputMonitor::start()
114 std::unique_lock<Mutex> mMutex;
115 setHandler(mDevicePath);
118 void InputMonitor::stop()
125 bool isDeviceWithName(const rgx::regex& deviceNameRegex,
126 const fs::directory_entry& directoryEntry)
128 std::string path = directoryEntry.path().string();
130 if (!utils::isCharDevice(path)) {
134 int fd = ::open(path.c_str(), O_RDONLY);
136 LOGD("Failed to open " << path);
140 char name[DEVICE_NAME_LENGTH];
141 if (::ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
142 LOGD("Failed to get the device name of: " << path);
143 if (::close(fd) < 0) {
144 LOGE("Error during closing file " << path);
149 if (::close(fd) < 0) {
150 LOGE("Error during closing file " << path);
153 LOGD("Checking device: " << name);
154 if (rgx::regex_match(name, deviceNameRegex)) {
155 LOGI("Device file found under: " << path);
164 std::string InputMonitor::getDevicePath() const
166 std::string device = mConfig.device;
167 if (fs::path(device).is_absolute()
168 && fs::exists(device)) {
169 LOGD("Device file path is given");
173 // device name is given - device file path is to be determined
174 LOGT("Determining, which device node is assigned to '" << device << "'");
175 using namespace std::placeholders;
176 fs::directory_iterator end;
177 rgx::regex deviceNameRegex(".*" + device + ".*");
178 const auto it = std::find_if(fs::directory_iterator(DEVICE_DIR),
180 std::bind(isDeviceWithName, deviceNameRegex, _1));
182 LOGE("None of the files under '" << DEVICE_DIR <<
183 "' represents device named: " << device);
184 throw InputMonitorException("Cannot find a device");
187 return it->path().string();
190 void InputMonitor::setHandler(const std::string& devicePath)
192 using namespace std::placeholders;
194 // We need NONBLOCK for FIFOs in the tests
195 mFd = ::open(devicePath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
197 const std::string msg = "Cannot create input monitor channel. Device file: " +
198 devicePath + " doesn't exist";
200 throw InputMonitorException(msg);
202 mEventPoll.addFD(mFd, EPOLLIN, std::bind(&InputMonitor::handleInternal, this, _1, _2));
205 void InputMonitor::handleInternal(int /* fd */, ipc::epoll::Events events)
207 struct input_event ie;
209 if (events == EPOLLHUP) {
213 utils::read(mFd, &ie, sizeof(struct input_event));
214 } catch (const std::exception& ex) {
215 LOGE("Read from input monitor channel failed: " << ex.what());
218 if (isExpectedEventSequence(ie)) {
219 LOGI("Input monitor detected pattern.");
220 if (mZonesManager->isRunning()) {
221 mZonesManager->switchingSequenceMonitorNotify();
226 void InputMonitor::leaveDevice()
229 mEventPoll.removeFD(mFd);
235 bool InputMonitor::isExpectedEventSequence(const struct input_event& ie)
237 LOGT("Event detected [" << mConfig.device.c_str() << "]:\n"
238 << "\ttime: " << ie.time.tv_sec << "." << ie.time.tv_usec << " sec\n"
239 << "\ttype, code, value: " << ie.type << ", " << ie.code << ", " << ie.value);
241 if (ie.type != EV_KEY
242 || ie.code != mConfig.code
243 || ie.value != KEY_PRESSED) {
244 LOGT("Wrong kind of event");
248 mEventTimes.push_back(ie.time);
250 if (mEventTimes.size() < static_cast<unsigned int>(mConfig.numberOfEvents)) {
251 LOGT("Event sequence too short");
255 struct timeval oldest = mEventTimes.front();
256 mEventTimes.pop_front();
257 struct timeval latest = mEventTimes.back();
259 long int secDiff = latest.tv_sec - oldest.tv_sec;
260 if (secDiff >= MAX_TIME_WINDOW_SEC) {
261 LOGT("Time window exceeded");
265 long int timeDiff = secDiff * 1000L;
266 timeDiff += static_cast<long int>((latest.tv_usec - oldest.tv_usec) / 1000L);
267 if (timeDiff < mConfig.timeWindowMs) {
268 LOGD("Event sequence detected");
273 LOGT("Event sequence not detected");