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