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 ::bind(mListenFd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
51 if (::listen(mListenFd, 0) != NO_ERROR) {
52 ALOGE("Cannot listen to port %d", port);
53 mListenFd = -1;
54 return;
55 }
56
57 std::ostringstream s;
58 s << "socket:" << port;
59 mDevice = s.str();
60 }
61
~SocketConnectionDetector()62 SocketConnectionDetector::~SocketConnectionDetector() {
63 if (mListenFd >= 0) {
64 requestExitAndWait();
65 }
66 }
67
Init()68 void SocketConnectionDetector::Init() {
69 // run adds a strong reference to this object, so it can't be invoked from
70 // the constructor.
71 run("ddad_socket");
72 }
73
waitForConnection()74 int SocketConnectionDetector::waitForConnection() {
75 return ::accept(mListenFd, nullptr, nullptr);
76 }
77
waitForDisconnection(int connFd)78 void SocketConnectionDetector::waitForDisconnection(int connFd) {
79 char buffer[16];
80 while (::read(connFd, buffer, sizeof(buffer)) > 0) {
81 // discard data but response something to denote thread alive
82 ::write(connFd, ".", 1);
83 }
84 // read failure means disconnection
85 ::close(connFd);
86 }
87
threadLoop()88 bool SocketConnectionDetector::threadLoop() {
89 while (!Thread::exitPending()) {
90 // block waiting for connection
91 int connFd = waitForConnection();
92
93 if (connFd < 0) {
94 break;
95 }
96
97 ALOGV("Received connection, register dynamic accel sensor");
98 mDaemon->onConnectionChange(mDevice, true);
99
100 waitForDisconnection(connFd);
101 ALOGV("Connection break, unregister dynamic accel sensor");
102 mDaemon->onConnectionChange(mDevice, false);
103 }
104 mDaemon->onConnectionChange(mDevice, false);
105 ALOGD("SocketConnectionDetector thread exited");
106 return false;
107 }
108
109 // FileConnectionDetector functions
FileConnectionDetector(BaseDynamicSensorDaemon * d,const std::string & path,const std::string & regex)110 FileConnectionDetector::FileConnectionDetector (
111 BaseDynamicSensorDaemon *d, const std::string &path, const std::string ®ex)
112 : ConnectionDetector(d), Thread(false /*callCallJava*/), mPath(path), mRegex(regex),
113 mLooper(new Looper(true /*allowNonCallback*/)), mInotifyFd(-1) {
114 if (mLooper == nullptr) {
115 return;
116 }
117
118 mInotifyFd = ::inotify_init1(IN_NONBLOCK);
119 if (mInotifyFd < 0) {
120 ALOGE("Cannot init inotify");
121 return;
122 }
123
124 int wd = ::inotify_add_watch(mInotifyFd, path.c_str(), IN_CREATE | IN_DELETE);
125 if (wd < 0 || !mLooper->addFd(mInotifyFd, POLL_IDENT, Looper::EVENT_INPUT, nullptr, nullptr)) {
126 ::close(mInotifyFd);
127 mInotifyFd = -1;
128 ALOGE("Cannot setup watch on dir %s", path.c_str());
129 return;
130 }
131 }
132
~FileConnectionDetector()133 FileConnectionDetector::~FileConnectionDetector() {
134 if (mInotifyFd > 0) {
135 requestExit();
136 mLooper->wake();
137 join();
138 ::close(mInotifyFd);
139 }
140 }
141
Init()142 void FileConnectionDetector::Init() {
143 // mLooper != null && mInotifyFd added to looper
144 // run adds a strong reference to this object, so it can't be invoked from
145 // the constructor.
146 run("ddad_file");
147 }
148
matches(const std::string & name) const149 bool FileConnectionDetector::matches(const std::string &name) const {
150 return std::regex_match(name, mRegex);
151 }
152
getFullName(const std::string name) const153 std::string FileConnectionDetector::getFullName(const std::string name) const {
154 return mPath + name;
155 }
156
processExistingFiles() const157 void FileConnectionDetector::processExistingFiles() const {
158 auto dirp = ::opendir(mPath.c_str());
159 struct dirent *dp;
160 while ((dp = ::readdir(dirp)) != NULL) {
161 const std::string name(dp->d_name);
162 if (matches(name)) {
163 mDaemon->onConnectionChange(getFullName(name), true /*connected*/);
164 }
165 }
166 ::closedir(dirp);
167 }
168
handleInotifyData(ssize_t len,const char * data)169 void FileConnectionDetector::handleInotifyData(ssize_t len, const char *data) {
170 const char *dataEnd = data + len;
171 const struct inotify_event *ev;
172
173 // inotify adds paddings to guarantee the next read is aligned
174 for (; data < dataEnd; data += sizeof(struct inotify_event) + ev->len) {
175 ev = reinterpret_cast<const struct inotify_event*>(data);
176 if (ev->mask & IN_ISDIR) {
177 continue;
178 }
179
180 const std::string name(ev->name);
181 if (matches(name)) {
182 if (ev->mask & IN_CREATE) {
183 mDaemon->onConnectionChange(getFullName(name), true /*connected*/);
184 }
185 if (ev->mask & IN_DELETE) {
186 mDaemon->onConnectionChange(getFullName(name), false /*connected*/);
187 }
188 }
189 }
190 }
191
readInotifyData()192 bool FileConnectionDetector::readInotifyData() {
193 struct {
194 struct inotify_event ev;
195 char padding[NAME_MAX + 1];
196 } buffer;
197
198 bool ret = true;
199 while (true) {
200 ssize_t len = ::read(mInotifyFd, &buffer, sizeof(buffer));
201 if (len == -1 && errno == EAGAIN) {
202 // no more data
203 break;
204 } else if (len > static_cast<ssize_t>(sizeof(struct inotify_event))) {
205 handleInotifyData(len, reinterpret_cast<char*>(&buffer));
206 } else if (len < 0) {
207 ALOGE("read error: %s", ::strerror(errno));
208 ret = false;
209 break;
210 } else {
211 // 0 <= len <= sizeof(struct inotify_event)
212 ALOGE("read return %zd, shorter than inotify_event size %zu",
213 len, sizeof(struct inotify_event));
214 ret = false;
215 break;
216 }
217 }
218 return ret;
219 }
220
threadLoop()221 bool FileConnectionDetector::threadLoop() {
222 Looper::setForThread(mLooper);
223 processExistingFiles();
224 while(!Thread::exitPending()) {
225 int ret = mLooper->pollOnce(-1);
226
227 if (ret != Looper::POLL_WAKE && ret != POLL_IDENT) {
228 ALOGE("Unexpected value %d from pollOnce, quit", ret);
229 requestExit();
230 break;
231 }
232
233 if (ret == POLL_IDENT) {
234 if (!readInotifyData()) {
235 requestExit();
236 }
237 }
238 }
239
240 mLooper->removeFd(mInotifyFd);
241 ALOGD("FileConnectionDetection thread exited");
242 return false;
243 }
244
245 } // namespace SensorHalExt
246 } // namespace android
247