1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "ConnectionDetector.h"
18
19 #include <utils/Log.h>
20
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <netinet/in.h>
24 #include <sys/inotify.h>
25 #include <sys/socket.h>
26
27 #include <sstream>
28
29 namespace android {
30 namespace SensorHalExt {
31
32 // SocketConnectionDetector functions
SocketConnectionDetector(BaseDynamicSensorDaemon * d,int port)33 SocketConnectionDetector::SocketConnectionDetector(BaseDynamicSensorDaemon *d, int port)
34 : ConnectionDetector(d), Thread(false /*canCallJava*/) {
35 // initialize socket that accept connection to localhost:port
36 mListenFd = ::socket(AF_INET, SOCK_STREAM, 0);
37 if (mListenFd < 0) {
38 ALOGE("Cannot open socket");
39 return;
40 }
41
42 struct sockaddr_in serverAddress = {
43 .sin_family = AF_INET,
44 .sin_port = htons(port),
45 .sin_addr = {
46 .s_addr = htonl(INADDR_LOOPBACK)
47 }
48 };
49
50 if (::bind(mListenFd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) != NO_ERROR) {
51 ALOGE("Cannot bind to port %d", port);
52 mListenFd = -1;
53 return;
54 }
55 if (::listen(mListenFd, 0) != NO_ERROR) {
56 ALOGE("Cannot listen to port %d", port);
57 mListenFd = -1;
58 return;
59 }
60
61 std::ostringstream s;
62 s << "socket:" << port;
63 mDevice = s.str();
64 }
65
~SocketConnectionDetector()66 SocketConnectionDetector::~SocketConnectionDetector() {
67 if (mListenFd >= 0) {
68 requestExitAndWait();
69 }
70 }
71
Init()72 void SocketConnectionDetector::Init() {
73 // run adds a strong reference to this object, so it can't be invoked from
74 // the constructor.
75 run("ddad_socket");
76 }
77
waitForConnection()78 int SocketConnectionDetector::waitForConnection() {
79 return ::accept(mListenFd, nullptr, nullptr);
80 }
81
waitForDisconnection(int connFd)82 void SocketConnectionDetector::waitForDisconnection(int connFd) {
83 char buffer[16];
84 while (::read(connFd, buffer, sizeof(buffer)) > 0) {
85 // discard data but response something to denote thread alive
86 ::write(connFd, ".", 1);
87 }
88 // read failure means disconnection
89 ::close(connFd);
90 }
91
threadLoop()92 bool SocketConnectionDetector::threadLoop() {
93 while (!Thread::exitPending()) {
94 // block waiting for connection
95 int connFd = waitForConnection();
96
97 if (connFd < 0) {
98 break;
99 }
100
101 ALOGV("Received connection, register dynamic accel sensor");
102 mDaemon->onConnectionChange(mDevice, true);
103
104 waitForDisconnection(connFd);
105 ALOGV("Connection break, unregister dynamic accel sensor");
106 mDaemon->onConnectionChange(mDevice, false);
107 }
108 mDaemon->onConnectionChange(mDevice, false);
109 ALOGD("SocketConnectionDetector thread exited");
110 return false;
111 }
112
113 // FileConnectionDetector functions
FileConnectionDetector(BaseDynamicSensorDaemon * d,const std::string & path,const std::string & regex)114 FileConnectionDetector::FileConnectionDetector (
115 BaseDynamicSensorDaemon *d, const std::string &path, const std::string ®ex)
116 : ConnectionDetector(d), Thread(false /*callCallJava*/), mPath(path), mRegex(regex),
117 mLooper(new Looper(true /*allowNonCallback*/)), mInotifyFd(-1) {
118 if (mLooper == nullptr) {
119 return;
120 }
121
122 mInotifyFd = ::inotify_init1(IN_NONBLOCK);
123 if (mInotifyFd < 0) {
124 ALOGE("Cannot init inotify");
125 return;
126 }
127
128 int wd = ::inotify_add_watch(mInotifyFd, path.c_str(), IN_CREATE | IN_DELETE);
129 if (wd < 0 || !mLooper->addFd(mInotifyFd, POLL_IDENT, Looper::EVENT_INPUT, nullptr, nullptr)) {
130 ::close(mInotifyFd);
131 mInotifyFd = -1;
132 ALOGE("Cannot setup watch on dir %s", path.c_str());
133 return;
134 }
135 }
136
~FileConnectionDetector()137 FileConnectionDetector::~FileConnectionDetector() {
138 if (mInotifyFd > 0) {
139 requestExit();
140 mLooper->wake();
141 join();
142 ::close(mInotifyFd);
143 }
144 }
145
Init()146 void FileConnectionDetector::Init() {
147 // mLooper != null && mInotifyFd added to looper
148 // run adds a strong reference to this object, so it can't be invoked from
149 // the constructor.
150 run("ddad_file");
151 }
152
matches(const std::string & name) const153 bool FileConnectionDetector::matches(const std::string &name) const {
154 return std::regex_match(name, mRegex);
155 }
156
getFullName(const std::string name) const157 std::string FileConnectionDetector::getFullName(const std::string name) const {
158 return mPath + name;
159 }
160
processExistingFiles() const161 void FileConnectionDetector::processExistingFiles() const {
162 auto dirp = ::opendir(mPath.c_str());
163 if(dirp == NULL) {
164 ALOGE("Problem open dir %s, errno: %s", mPath.c_str(), ::strerror(errno));
165 return;
166 }
167 struct dirent *dp;
168 while ((dp = ::readdir(dirp)) != NULL) {
169 const std::string name(dp->d_name);
170 if (matches(name)) {
171 mDaemon->onConnectionChange(getFullName(name), true /*connected*/);
172 }
173 }
174 ::closedir(dirp);
175 }
176
handleInotifyData(ssize_t len,const char * data)177 void FileConnectionDetector::handleInotifyData(ssize_t len, const char *data) {
178 const char *dataEnd = data + len;
179 const struct inotify_event *ev;
180
181 // inotify adds paddings to guarantee the next read is aligned
182 for (; data < dataEnd; data += sizeof(struct inotify_event) + ev->len) {
183 ev = reinterpret_cast<const struct inotify_event*>(data);
184 if (ev->mask & IN_ISDIR) {
185 continue;
186 }
187
188 const std::string name(ev->name);
189 if (matches(name)) {
190 if (ev->mask & IN_CREATE) {
191 mDaemon->onConnectionChange(getFullName(name), true /*connected*/);
192 }
193 if (ev->mask & IN_DELETE) {
194 mDaemon->onConnectionChange(getFullName(name), false /*connected*/);
195 }
196 }
197 }
198 }
199
readInotifyData()200 bool FileConnectionDetector::readInotifyData() {
201 union {
202 struct inotify_event ev;
203 char raw[sizeof(inotify_event) + NAME_MAX + 1] = {0};
204 } buffer;
205
206 bool ret = true;
207 while (true) {
208 ssize_t len = ::read(mInotifyFd, &buffer, sizeof(buffer) - sizeof(char));
209 if (len == -1 && errno == EAGAIN) {
210 // no more data
211 break;
212 } else if (len > static_cast<ssize_t>(sizeof(struct inotify_event))) {
213 handleInotifyData(len, reinterpret_cast<char*>(&buffer));
214 } else if (len < 0) {
215 ALOGE("read error: %s", ::strerror(errno));
216 ret = false;
217 break;
218 } else {
219 // 0 <= len <= sizeof(struct inotify_event)
220 ALOGE("read return %zd, shorter than inotify_event size %zu",
221 len, sizeof(struct inotify_event));
222 ret = false;
223 break;
224 }
225 }
226 return ret;
227 }
228
threadLoop()229 bool FileConnectionDetector::threadLoop() {
230 Looper::setForThread(mLooper);
231 processExistingFiles();
232 while(!Thread::exitPending()) {
233 int ret = mLooper->pollOnce(-1);
234
235 if (ret != Looper::POLL_WAKE && ret != POLL_IDENT) {
236 ALOGE("Unexpected value %d from pollOnce, quit", ret);
237 requestExit();
238 break;
239 }
240
241 if (ret == POLL_IDENT) {
242 if (!readInotifyData()) {
243 requestExit();
244 }
245 }
246 }
247
248 mLooper->removeFd(mInotifyFd);
249 ALOGD("FileConnectionDetection thread exited");
250 return false;
251 }
252
253 } // namespace SensorHalExt
254 } // namespace android
255