1 /*
2  * Copyright (C) 2020 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 <log/log.h>
18 #include <fcntl.h>
19 #include <qemud.h>
20 #include <qemu_pipe_bp.h>
21 #include <sys/epoll.h>
22 #include <sys/socket.h>
23 #include <debug.h>
24 #include "GnssHwConn.h"
25 #include "GnssHwListener.h"
26 
27 namespace aidl {
28 namespace android {
29 namespace hardware {
30 namespace gnss {
31 namespace implementation {
32 namespace {
33 constexpr char kCMD_QUIT = 'q';
34 
epollCtlAdd(int epollFd,int fd)35 int epollCtlAdd(int epollFd, int fd) {
36     int ret;
37 
38     /* make the fd non-blocking */
39     ret = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
40     if (ret < 0) {
41         return FAILURE(ret);
42     }
43     ret = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, ret | O_NONBLOCK));
44     if (ret < 0) {
45         return FAILURE(ret);
46     }
47 
48     struct epoll_event ev;
49     ev.events  = EPOLLIN;
50     ev.data.fd = fd;
51 
52     ret = TEMP_FAILURE_RETRY(epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ev));
53     if (ret < 0) {
54         return FAILURE(ret);
55     }
56 
57     return 0;
58 }
59 
workerThreadRcvCommand(const int fd)60 int workerThreadRcvCommand(const int fd) {
61     char buf;
62     if (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) {
63         return buf;
64     } else {
65         return FAILURE(-1);
66     }
67 }
68 
workerThread(const int devFd,const int threadsFd,GnssHwListener & listener)69 void workerThread(const int devFd, const int threadsFd, GnssHwListener& listener) {
70     ALOGD("%s:%s:%d", "GnssHwConn", __func__, __LINE__);
71 
72     const unique_fd epollFd(epoll_create1(0));
73     LOG_ALWAYS_FATAL_IF(!epollFd.ok(), "%s:%d: epoll_create1 failed",
74                         __func__, __LINE__);
75 
76     epollCtlAdd(epollFd.get(), devFd);
77     epollCtlAdd(epollFd.get(), threadsFd);
78 
79     while (true) {
80         struct epoll_event events[2];
81         const int kTimeoutMs = 60000;
82         const int n = TEMP_FAILURE_RETRY(epoll_wait(epollFd.get(),
83                                                     events, 2,
84                                                     kTimeoutMs));
85         if (n < 0) {
86             ALOGE("%s:%d: epoll_wait failed with '%s'",
87                   __func__, __LINE__, strerror(errno));
88             continue;
89         }
90 
91         for (int i = 0; i < n; ++i) {
92             const struct epoll_event* ev = &events[i];
93             const int fd = ev->data.fd;
94             const int ev_events = ev->events;
95 
96             if (fd == devFd) {
97                 if (ev_events & (EPOLLERR | EPOLLHUP)) {
98                     LOG_ALWAYS_FATAL("%s:%d: epoll_wait: devFd has an error, "
99                                      "ev_events=%x", __func__, __LINE__, ev_events);
100                 } else if (ev_events & EPOLLIN) {
101                     char buf[64];
102                     while (true) {
103                         int n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
104                         if (n > 0) {
105                             listener.consume(buf, n);
106                         } else {
107                             break;
108                         }
109                     }
110                 }
111             } else if (fd == threadsFd) {
112                 if (ev_events & (EPOLLERR | EPOLLHUP)) {
113                     LOG_ALWAYS_FATAL("%s:%d: epoll_wait: threadsFd has an error, "
114                                      "ev_events=%x", __func__, __LINE__, ev_events);
115                 } else if (ev_events & EPOLLIN) {
116                     const int cmd = workerThreadRcvCommand(fd);
117                     switch (cmd) {
118                         case kCMD_QUIT:
119                             ALOGD("%s:%s:%d", "GnssHwConn", __func__, __LINE__);
120                             return;
121 
122                         default:
123                             LOG_ALWAYS_FATAL("%s:%d: workerThreadRcvCommand returned "
124                                              "unexpected command, cmd=%d",
125                                              __func__, __LINE__, cmd);
126                             break;
127                     }
128                 }
129             } else {
130                 ALOGE("%s:%d: epoll_wait() returned unexpected fd",
131                       __func__, __LINE__);
132             }
133         }
134     }
135 }
136 
137 }  // namespace
138 
GnssHwConn(IDataSink & sink)139 GnssHwConn::GnssHwConn(IDataSink& sink) {
140     mDevFd.reset(qemu_pipe_open_ns("qemud", "gps", O_RDWR));
141     if (!mDevFd.ok()) {
142         ALOGE("%s:%d: qemu_pipe_open_ns failed", __func__, __LINE__);
143         return;
144     }
145 
146     unique_fd threadsFd;
147     if (!::android::base::Socketpair(AF_LOCAL, SOCK_STREAM, 0,
148                                      &mCallersFd, &threadsFd)) {
149         ALOGE("%s:%d: Socketpair failed", __func__, __LINE__);
150         mDevFd.reset();
151         return;
152     }
153 
154     std::promise<void> isReadyPromise;
155     const int devFd = mDevFd.get();
156     mThread = std::thread([devFd, threadsFd = std::move(threadsFd), &sink,
157                            &isReadyPromise]() {
158         GnssHwListener listener(sink);
159         isReadyPromise.set_value();
160         workerThread(devFd, threadsFd.get(), listener);
161     });
162 
163     isReadyPromise.get_future().wait();
164 }
165 
~GnssHwConn()166 GnssHwConn::~GnssHwConn() {
167     if (mThread.joinable()) {
168         sendWorkerThreadCommand(kCMD_QUIT);
169         mThread.join();
170     }
171 }
172 
ok() const173 bool GnssHwConn::ok() const {
174     return mThread.joinable();
175 }
176 
sendWorkerThreadCommand(char cmd) const177 bool GnssHwConn::sendWorkerThreadCommand(char cmd) const {
178     return (TEMP_FAILURE_RETRY(write(mCallersFd.get(), &cmd, 1)) == 1) ?
179         true : FAILURE(false);
180 }
181 
182 }  // namespace implementation
183 }  // namespace gnss
184 }  // namespace hardware
185 }  // namespace android
186 }  // namespace aidl
187