lxcpp: fix cgroup unit tests
[platform/core/security/vasum.git] / server / input-monitor.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak <j.olszak@samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18
19 /**
20  * @file
21  * @author  Jan Olszak (j.olszak@samsung.com)
22  * @brief   C++ wrapper for glib input monitor
23  */
24
25 #include "config.hpp"
26
27 #include "input-monitor-config.hpp"
28 #include "input-monitor.hpp"
29 #include "zones-manager.hpp"
30 #include "exception.hpp"
31
32 #include "logger/logger.hpp"
33 #include "utils/exception.hpp"
34 #include "utils/fs.hpp"
35 #include "utils/fd-utils.hpp"
36
37 #include <cassert>
38 #include <cstring>
39 #include <fcntl.h>
40 #include <linux/input.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44
45 #include <boost/filesystem.hpp>
46 #include <vector>
47 #include <functional>
48
49 #ifdef USE_BOOST_REGEX
50 #include <boost/regex.hpp>
51 namespace rgx = boost;
52 #else
53 #include <regex>
54 namespace rgx = std;
55 #endif
56
57 using namespace utils;
58 namespace fs = boost::filesystem;
59
60 namespace vasum {
61
62 namespace {
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/";
68
69 } // namespace
70
71
72 InputMonitor::InputMonitor(ipc::epoll::EventPoll& eventPoll,
73                            const InputConfig& inputConfig,
74                            ZonesManager* zonesManager)
75     : mConfig(inputConfig)
76     , mZonesManager(zonesManager)
77     , mFd(-1)
78     , mEventPoll(eventPoll)
79 {
80     if (mConfig.timeWindowMs > MAX_TIME_WINDOW_SEC * 1000L) {
81         const std::string msg = "Time window exceeds maximum: " + MAX_TIME_WINDOW_SEC;
82         LOGE(msg);
83         throw TimeoutException(msg);
84     }
85
86     if (mConfig.numberOfEvents > MAX_NUMBER_OF_EVENTS) {
87         const std::string msg = "Number of events exceeds maximum: " + MAX_NUMBER_OF_EVENTS;
88         LOGE(msg);
89         throw InputMonitorException(msg);
90     }
91
92     mDevicePath = getDevicePath();
93
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);
103 }
104
105 InputMonitor::~InputMonitor()
106 {
107     std::unique_lock<std::mutex> mMutex;
108     LOGD("Destroying InputMonitor");
109     stop();
110 }
111
112 void InputMonitor::start()
113 {
114     std::unique_lock<Mutex> mMutex;
115     setHandler(mDevicePath);
116 }
117
118 void InputMonitor::stop()
119 {
120     leaveDevice();
121 }
122
123 namespace {
124
125 bool isDeviceWithName(const rgx::regex& deviceNameRegex,
126                       const fs::directory_entry& directoryEntry)
127 {
128     std::string path = directoryEntry.path().string();
129
130     if (!utils::isCharDevice(path)) {
131         return false;
132     }
133
134     int fd = ::open(path.c_str(), O_RDONLY);
135     if (fd < 0) {
136         LOGD("Failed to open " << path);
137         return false;
138     }
139
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);
145         }
146         return false;
147     }
148
149     if (::close(fd) < 0) {
150         LOGE("Error during closing file " << path);
151     }
152
153     LOGD("Checking device: " << name);
154     if (rgx::regex_match(name, deviceNameRegex)) {
155         LOGI("Device file found under: " << path);
156         return true;
157     }
158
159     return false;
160 }
161
162 } // namespace
163
164 std::string InputMonitor::getDevicePath() const
165 {
166     std::string device = mConfig.device;
167     if (fs::path(device).is_absolute()
168             && fs::exists(device)) {
169         LOGD("Device file path is given");
170         return device;
171     }
172
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),
179                                  end,
180                                  std::bind(isDeviceWithName, deviceNameRegex, _1));
181     if (it == end) {
182         LOGE("None of the files under '" << DEVICE_DIR <<
183              "' represents device named: " << device);
184         throw InputMonitorException("Cannot find a device");
185     }
186
187     return it->path().string();
188 }
189
190 void InputMonitor::setHandler(const std::string& devicePath)
191 {
192     using namespace std::placeholders;
193
194     // We need NONBLOCK for FIFOs in the tests
195     mFd = ::open(devicePath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
196     if (mFd < 0) {
197         const std::string msg = "Cannot create input monitor channel. Device file: " +
198                                 devicePath + " doesn't exist";
199         LOGE(msg);
200         throw InputMonitorException(msg);
201     }
202     mEventPoll.addFD(mFd, EPOLLIN, std::bind(&InputMonitor::handleInternal, this, _1, _2));
203 }
204
205 void InputMonitor::handleInternal(int /* fd */, ipc::epoll::Events events)
206 {
207     struct input_event ie;
208     try {
209         if (events == EPOLLHUP) {
210             stop();
211             return;
212         }
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());
216         return;
217     }
218     if (isExpectedEventSequence(ie)) {
219         LOGI("Input monitor detected pattern.");
220         if (mZonesManager->isRunning()) {
221             mZonesManager->switchingSequenceMonitorNotify();
222         }
223     }
224 }
225
226 void InputMonitor::leaveDevice()
227 {
228     if (mFd != -1) {
229         mEventPoll.removeFD(mFd);
230         utils::close(mFd);
231         mFd = -1;
232     }
233 }
234
235 bool InputMonitor::isExpectedEventSequence(const struct input_event& ie)
236 {
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);
240
241     if (ie.type != EV_KEY
242             || ie.code != mConfig.code
243             || ie.value != KEY_PRESSED) {
244         LOGT("Wrong kind of event");
245         return false;
246     }
247
248     mEventTimes.push_back(ie.time);
249
250     if (mEventTimes.size() < static_cast<unsigned int>(mConfig.numberOfEvents)) {
251         LOGT("Event sequence too short");
252         return false;
253     }
254
255     struct timeval oldest = mEventTimes.front();
256     mEventTimes.pop_front();
257     struct timeval latest = mEventTimes.back();
258
259     long int secDiff = latest.tv_sec - oldest.tv_sec;
260     if (secDiff >= MAX_TIME_WINDOW_SEC) {
261         LOGT("Time window exceeded");
262         return false;
263     }
264
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");
269         mEventTimes.clear();
270         return true;
271     }
272
273     LOGT("Event sequence not detected");
274     return false;
275 }
276 } // namespace vasum