Home | History | Annotate | Download | only in dynamic_sensor
      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 &regex)
    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