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>
46 #include <boost/regex.hpp>
50 using namespace utils;
51 namespace fs = boost::filesystem;
56 const int MAX_TIME_WINDOW_SEC = 10;
57 const int KEY_PRESSED = 1;
58 const int DEVICE_NAME_LENGTH = 256;
59 const int MAX_NUMBER_OF_EVENTS = 10;
60 const std::string DEVICE_DIR = "/dev/input/";
65 InputMonitor::InputMonitor(ipc::epoll::EventPoll& eventPoll,
66 const InputConfig& inputConfig,
67 ZonesManager* zonesManager)
68 : mConfig(inputConfig)
69 , mZonesManager(zonesManager)
71 , mEventPoll(eventPoll)
73 if (mConfig.timeWindowMs > MAX_TIME_WINDOW_SEC * 1000L) {
74 const std::string msg = "Time window exceeds maximum: " + MAX_TIME_WINDOW_SEC;
76 throw TimeoutException(msg);
79 if (mConfig.numberOfEvents > MAX_NUMBER_OF_EVENTS) {
80 const std::string msg = "Number of events exceeds maximum: " + MAX_NUMBER_OF_EVENTS;
82 throw InputMonitorException(msg);
85 mDevicePath = getDevicePath();
87 LOGT("Input monitor configuration: \n"
88 << "\tenabled: " << mConfig.enabled << "\n"
89 << "\tdevice: " << mConfig.device << "\n"
90 << "\tpath: " << mDevicePath << "\n"
91 << "\ttype: " << EV_KEY << "\n"
92 << "\tcode: " << mConfig.code << "\n"
93 << "\tvalue: " << KEY_PRESSED << "\n"
94 << "\tnumberOfEvents: " << mConfig.numberOfEvents << "\n"
95 << "\ttimeWindowMs: " << mConfig.timeWindowMs);
98 InputMonitor::~InputMonitor()
100 std::unique_lock<std::mutex> mMutex;
101 LOGD("Destroying InputMonitor");
105 void InputMonitor::start()
107 std::unique_lock<Mutex> mMutex;
108 setHandler(mDevicePath);
111 void InputMonitor::stop()
118 bool isDeviceWithName(const boost::regex& deviceNameRegex,
119 const fs::directory_entry& directoryEntry)
121 std::string path = directoryEntry.path().string();
123 if (!utils::isCharDevice(path)) {
127 int fd = ::open(path.c_str(), O_RDONLY);
129 LOGD("Failed to open " << path);
133 char name[DEVICE_NAME_LENGTH];
134 if (::ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
135 LOGD("Failed to get the device name of: " << path);
136 if (::close(fd) < 0) {
137 LOGE("Error during closing file " << path);
142 if (::close(fd) < 0) {
143 LOGE("Error during closing file " << path);
146 LOGD("Checking device: " << name);
147 if (boost::regex_match(name, deviceNameRegex)) {
148 LOGI("Device file found under: " << path);
157 std::string InputMonitor::getDevicePath() const
159 std::string device = mConfig.device;
160 if (fs::path(device).is_absolute()
161 && fs::exists(device)) {
162 LOGD("Device file path is given");
166 // device name is given - device file path is to be determined
167 LOGT("Determining, which device node is assigned to '" << device << "'");
168 using namespace std::placeholders;
169 fs::directory_iterator end;
170 boost::regex deviceNameRegex(".*" + device + ".*");
171 const auto it = std::find_if(fs::directory_iterator(DEVICE_DIR),
173 std::bind(isDeviceWithName, deviceNameRegex, _1));
175 LOGE("None of the files under '" << DEVICE_DIR <<
176 "' represents device named: " << device);
177 throw InputMonitorException("Cannot find a device");
180 return it->path().string();
183 void InputMonitor::setHandler(const std::string& devicePath)
185 using namespace std::placeholders;
187 // We need NONBLOCK for FIFOs in the tests
188 mFd = ::open(devicePath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
190 const std::string msg = "Cannot create input monitor channel. Device file: " +
191 devicePath + " doesn't exist";
193 throw InputMonitorException(msg);
195 mEventPoll.addFD(mFd, EPOLLIN, std::bind(&InputMonitor::handleInternal, this, _1, _2));
198 void InputMonitor::handleInternal(int /* fd */, ipc::epoll::Events events)
200 struct input_event ie;
202 if (events == EPOLLHUP) {
206 utils::read(mFd, &ie, sizeof(struct input_event));
207 } catch (const std::exception& ex) {
208 LOGE("Read from input monitor channel failed: " << ex.what());
211 if (isExpectedEventSequence(ie)) {
212 LOGI("Input monitor detected pattern.");
213 if (mZonesManager->isRunning()) {
214 mZonesManager->switchingSequenceMonitorNotify();
219 void InputMonitor::leaveDevice()
222 mEventPoll.removeFD(mFd);
228 bool InputMonitor::isExpectedEventSequence(const struct input_event& ie)
230 LOGT("Event detected [" << mConfig.device.c_str() << "]:\n"
231 << "\ttime: " << ie.time.tv_sec << "." << ie.time.tv_usec << " sec\n"
232 << "\ttype, code, value: " << ie.type << ", " << ie.code << ", " << ie.value);
234 if (ie.type != EV_KEY
235 || ie.code != mConfig.code
236 || ie.value != KEY_PRESSED) {
237 LOGT("Wrong kind of event");
241 mEventTimes.push_back(ie.time);
243 if (mEventTimes.size() < static_cast<unsigned int>(mConfig.numberOfEvents)) {
244 LOGT("Event sequence too short");
248 struct timeval oldest = mEventTimes.front();
249 mEventTimes.pop_front();
250 struct timeval latest = mEventTimes.back();
252 long int secDiff = latest.tv_sec - oldest.tv_sec;
253 if (secDiff >= MAX_TIME_WINDOW_SEC) {
254 LOGT("Time window exceeded");
258 long int timeDiff = secDiff * 1000L;
259 timeDiff += static_cast<long int>((latest.tv_usec - oldest.tv_usec) / 1000L);
260 if (timeDiff < mConfig.timeWindowMs) {
261 LOGD("Event sequence detected");
266 LOGT("Event sequence not detected");