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