lxcpp: provisioning implementation (part 2)
[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 <boost/regex.hpp>
47 #include <vector>
48 #include <functional>
49
50 using namespace utils;
51 namespace fs = boost::filesystem;
52
53 namespace vasum {
54
55 namespace {
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/";
61
62 } // namespace
63
64
65 InputMonitor::InputMonitor(ipc::epoll::EventPoll& eventPoll,
66                            const InputConfig& inputConfig,
67                            ZonesManager* zonesManager)
68     : mConfig(inputConfig)
69     , mZonesManager(zonesManager)
70     , mFd(-1)
71     , mEventPoll(eventPoll)
72 {
73     if (mConfig.timeWindowMs > MAX_TIME_WINDOW_SEC * 1000L) {
74         const std::string msg = "Time window exceeds maximum: " + MAX_TIME_WINDOW_SEC;
75         LOGE(msg);
76         throw TimeoutException(msg);
77     }
78
79     if (mConfig.numberOfEvents > MAX_NUMBER_OF_EVENTS) {
80         const std::string msg = "Number of events exceeds maximum: " + MAX_NUMBER_OF_EVENTS;
81         LOGE(msg);
82         throw InputMonitorException(msg);
83     }
84
85     mDevicePath = getDevicePath();
86
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);
96 }
97
98 InputMonitor::~InputMonitor()
99 {
100     std::unique_lock<std::mutex> mMutex;
101     LOGD("Destroying InputMonitor");
102     stop();
103 }
104
105 void InputMonitor::start()
106 {
107     std::unique_lock<Mutex> mMutex;
108     setHandler(mDevicePath);
109 }
110
111 void InputMonitor::stop()
112 {
113     leaveDevice();
114 }
115
116 namespace {
117
118 bool isDeviceWithName(const boost::regex& deviceNameRegex,
119                       const fs::directory_entry& directoryEntry)
120 {
121     std::string path = directoryEntry.path().string();
122
123     if (!utils::isCharDevice(path)) {
124         return false;
125     }
126
127     int fd = ::open(path.c_str(), O_RDONLY);
128     if (fd < 0) {
129         LOGD("Failed to open " << path);
130         return false;
131     }
132
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);
138         }
139         return false;
140     }
141
142     if (::close(fd) < 0) {
143         LOGE("Error during closing file " << path);
144     }
145
146     LOGD("Checking device: " << name);
147     if (boost::regex_match(name, deviceNameRegex)) {
148         LOGI("Device file found under: " << path);
149         return true;
150     }
151
152     return false;
153 }
154
155 } // namespace
156
157 std::string InputMonitor::getDevicePath() const
158 {
159     std::string device = mConfig.device;
160     if (fs::path(device).is_absolute()
161             && fs::exists(device)) {
162         LOGD("Device file path is given");
163         return device;
164     }
165
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),
172                                  end,
173                                  std::bind(isDeviceWithName, deviceNameRegex, _1));
174     if (it == end) {
175         LOGE("None of the files under '" << DEVICE_DIR <<
176              "' represents device named: " << device);
177         throw InputMonitorException("Cannot find a device");
178     }
179
180     return it->path().string();
181 }
182
183 void InputMonitor::setHandler(const std::string& devicePath)
184 {
185     using namespace std::placeholders;
186
187     // We need NONBLOCK for FIFOs in the tests
188     mFd = ::open(devicePath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
189     if (mFd < 0) {
190         const std::string msg = "Cannot create input monitor channel. Device file: " +
191                                 devicePath + " doesn't exist";
192         LOGE(msg);
193         throw InputMonitorException(msg);
194     }
195     mEventPoll.addFD(mFd, EPOLLIN, std::bind(&InputMonitor::handleInternal, this, _1, _2));
196 }
197
198 void InputMonitor::handleInternal(int /* fd */, ipc::epoll::Events events)
199 {
200     struct input_event ie;
201     try {
202         if (events == EPOLLHUP) {
203             stop();
204             return;
205         }
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());
209         return;
210     }
211     if (isExpectedEventSequence(ie)) {
212         LOGI("Input monitor detected pattern.");
213         if (mZonesManager->isRunning()) {
214             mZonesManager->switchingSequenceMonitorNotify();
215         }
216     }
217 }
218
219 void InputMonitor::leaveDevice()
220 {
221     if (mFd != -1) {
222         mEventPoll.removeFD(mFd);
223         utils::close(mFd);
224         mFd = -1;
225     }
226 }
227
228 bool InputMonitor::isExpectedEventSequence(const struct input_event& ie)
229 {
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);
233
234     if (ie.type != EV_KEY
235             || ie.code != mConfig.code
236             || ie.value != KEY_PRESSED) {
237         LOGT("Wrong kind of event");
238         return false;
239     }
240
241     mEventTimes.push_back(ie.time);
242
243     if (mEventTimes.size() < static_cast<unsigned int>(mConfig.numberOfEvents)) {
244         LOGT("Event sequence too short");
245         return false;
246     }
247
248     struct timeval oldest = mEventTimes.front();
249     mEventTimes.pop_front();
250     struct timeval latest = mEventTimes.back();
251
252     long int secDiff = latest.tv_sec - oldest.tv_sec;
253     if (secDiff >= MAX_TIME_WINDOW_SEC) {
254         LOGT("Time window exceeded");
255         return false;
256     }
257
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");
262         mEventTimes.clear();
263         return true;
264     }
265
266     LOGT("Event sequence not detected");
267     return false;
268 }
269 } // namespace vasum