| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "ConnectionDetector.h" |
| |
| #include <utils/Log.h> |
| |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <netinet/in.h> |
| #include <sys/inotify.h> |
| #include <sys/socket.h> |
| |
| #include <sstream> |
| |
| namespace android { |
| namespace SensorHalExt { |
| |
| // SocketConnectionDetector functions |
| SocketConnectionDetector::SocketConnectionDetector(BaseDynamicSensorDaemon *d, int port) |
| : ConnectionDetector(d), Thread(false /*canCallJava*/) { |
| // initialize socket that accept connection to localhost:port |
| mListenFd = ::socket(AF_INET, SOCK_STREAM, 0); |
| if (mListenFd < 0) { |
| ALOGE("Cannot open socket"); |
| return; |
| } |
| |
| struct sockaddr_in serverAddress = { |
| .sin_family = AF_INET, |
| .sin_port = htons(port), |
| .sin_addr = { |
| .s_addr = htonl(INADDR_LOOPBACK) |
| } |
| }; |
| |
| ::bind(mListenFd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)); |
| if (::listen(mListenFd, 0) != NO_ERROR) { |
| ALOGE("Cannot listen to port %d", port); |
| mListenFd = -1; |
| return; |
| } |
| |
| std::ostringstream s; |
| s << "socket:" << port; |
| mDevice = s.str(); |
| } |
| |
| SocketConnectionDetector::~SocketConnectionDetector() { |
| if (mListenFd >= 0) { |
| requestExitAndWait(); |
| } |
| } |
| |
| void SocketConnectionDetector::Init() { |
| // run adds a strong reference to this object, so it can't be invoked from |
| // the constructor. |
| run("ddad_socket"); |
| } |
| |
| int SocketConnectionDetector::waitForConnection() { |
| return ::accept(mListenFd, nullptr, nullptr); |
| } |
| |
| void SocketConnectionDetector::waitForDisconnection(int connFd) { |
| char buffer[16]; |
| while (::read(connFd, buffer, sizeof(buffer)) > 0) { |
| // discard data but response something to denote thread alive |
| ::write(connFd, ".", 1); |
| } |
| // read failure means disconnection |
| ::close(connFd); |
| } |
| |
| bool SocketConnectionDetector::threadLoop() { |
| while (!Thread::exitPending()) { |
| // block waiting for connection |
| int connFd = waitForConnection(); |
| |
| if (connFd < 0) { |
| break; |
| } |
| |
| ALOGV("Received connection, register dynamic accel sensor"); |
| mDaemon->onConnectionChange(mDevice, true); |
| |
| waitForDisconnection(connFd); |
| ALOGV("Connection break, unregister dynamic accel sensor"); |
| mDaemon->onConnectionChange(mDevice, false); |
| } |
| mDaemon->onConnectionChange(mDevice, false); |
| ALOGD("SocketConnectionDetector thread exited"); |
| return false; |
| } |
| |
| // FileConnectionDetector functions |
| FileConnectionDetector::FileConnectionDetector ( |
| BaseDynamicSensorDaemon *d, const std::string &path, const std::string ®ex) |
| : ConnectionDetector(d), Thread(false /*callCallJava*/), mPath(path), mRegex(regex), |
| mLooper(new Looper(true /*allowNonCallback*/)), mInotifyFd(-1) { |
| if (mLooper == nullptr) { |
| return; |
| } |
| |
| mInotifyFd = ::inotify_init1(IN_NONBLOCK); |
| if (mInotifyFd < 0) { |
| ALOGE("Cannot init inotify"); |
| return; |
| } |
| |
| int wd = ::inotify_add_watch(mInotifyFd, path.c_str(), IN_CREATE | IN_DELETE); |
| if (wd < 0 || !mLooper->addFd(mInotifyFd, POLL_IDENT, Looper::EVENT_INPUT, nullptr, nullptr)) { |
| ::close(mInotifyFd); |
| mInotifyFd = -1; |
| ALOGE("Cannot setup watch on dir %s", path.c_str()); |
| return; |
| } |
| } |
| |
| FileConnectionDetector::~FileConnectionDetector() { |
| if (mInotifyFd > 0) { |
| requestExit(); |
| mLooper->wake(); |
| join(); |
| ::close(mInotifyFd); |
| } |
| } |
| |
| void FileConnectionDetector::Init() { |
| // mLooper != null && mInotifyFd added to looper |
| // run adds a strong reference to this object, so it can't be invoked from |
| // the constructor. |
| run("ddad_file"); |
| } |
| |
| bool FileConnectionDetector::matches(const std::string &name) const { |
| return std::regex_match(name, mRegex); |
| } |
| |
| std::string FileConnectionDetector::getFullName(const std::string name) const { |
| return mPath + name; |
| } |
| |
| void FileConnectionDetector::processExistingFiles() const { |
| auto dirp = ::opendir(mPath.c_str()); |
| if(dirp == NULL) { |
| ALOGE("Problem open dir %s, errno: %s", mPath.c_str(), ::strerror(errno)); |
| return; |
| } |
| struct dirent *dp; |
| while ((dp = ::readdir(dirp)) != NULL) { |
| const std::string name(dp->d_name); |
| if (matches(name)) { |
| mDaemon->onConnectionChange(getFullName(name), true /*connected*/); |
| } |
| } |
| ::closedir(dirp); |
| } |
| |
| void FileConnectionDetector::handleInotifyData(ssize_t len, const char *data) { |
| const char *dataEnd = data + len; |
| const struct inotify_event *ev; |
| |
| // inotify adds paddings to guarantee the next read is aligned |
| for (; data < dataEnd; data += sizeof(struct inotify_event) + ev->len) { |
| ev = reinterpret_cast<const struct inotify_event*>(data); |
| if (ev->mask & IN_ISDIR) { |
| continue; |
| } |
| |
| const std::string name(ev->name); |
| if (matches(name)) { |
| if (ev->mask & IN_CREATE) { |
| mDaemon->onConnectionChange(getFullName(name), true /*connected*/); |
| } |
| if (ev->mask & IN_DELETE) { |
| mDaemon->onConnectionChange(getFullName(name), false /*connected*/); |
| } |
| } |
| } |
| } |
| |
| bool FileConnectionDetector::readInotifyData() { |
| struct { |
| struct inotify_event ev; |
| char padding[NAME_MAX + 1]; |
| } buffer; |
| |
| bool ret = true; |
| while (true) { |
| ssize_t len = ::read(mInotifyFd, &buffer, sizeof(buffer)); |
| if (len == -1 && errno == EAGAIN) { |
| // no more data |
| break; |
| } else if (len > static_cast<ssize_t>(sizeof(struct inotify_event))) { |
| handleInotifyData(len, reinterpret_cast<char*>(&buffer)); |
| } else if (len < 0) { |
| ALOGE("read error: %s", ::strerror(errno)); |
| ret = false; |
| break; |
| } else { |
| // 0 <= len <= sizeof(struct inotify_event) |
| ALOGE("read return %zd, shorter than inotify_event size %zu", |
| len, sizeof(struct inotify_event)); |
| ret = false; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| bool FileConnectionDetector::threadLoop() { |
| Looper::setForThread(mLooper); |
| processExistingFiles(); |
| while(!Thread::exitPending()) { |
| int ret = mLooper->pollOnce(-1); |
| |
| if (ret != Looper::POLL_WAKE && ret != POLL_IDENT) { |
| ALOGE("Unexpected value %d from pollOnce, quit", ret); |
| requestExit(); |
| break; |
| } |
| |
| if (ret == POLL_IDENT) { |
| if (!readInotifyData()) { |
| requestExit(); |
| } |
| } |
| } |
| |
| mLooper->removeFd(mInotifyFd); |
| ALOGD("FileConnectionDetection thread exited"); |
| return false; |
| } |
| |
| } // namespace SensorHalExt |
| } // namespace android |