• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &regex)
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