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 &regex)
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