1 /*
2  * Copyright (C) 2022 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 "exynos_daemon.h"
18 #include <sys/epoll.h>
19 #include <utils/SystemClock.h>
20 #include <array>
21 #include <csignal>
22 #include "chre_host/file_stream.h"
23 
24 // Aliased for consistency with the way these symbols are referenced in
25 // CHRE-side code
26 namespace fbs = ::chre::fbs;
27 
28 namespace android {
29 namespace chre {
30 
31 namespace {
32 
createEpollFd(int fdToEpoll)33 int createEpollFd(int fdToEpoll) {
34   struct epoll_event event;
35   event.data.fd = fdToEpoll;
36   event.events = EPOLLIN | EPOLLWAKEUP;
37   int epollFd = epoll_create1(EPOLL_CLOEXEC);
38   if (epoll_ctl(epollFd, EPOLL_CTL_ADD, event.data.fd, &event) != 0) {
39     LOGE("Failed to add control interface to msg read fd errno: %s",
40          strerror(errno));
41     epollFd = -1;
42   }
43   return epollFd;
44 }
45 
46 }  // anonymous namespace
47 
ExynosDaemon()48 ExynosDaemon::ExynosDaemon() : mLpmaHandler(true /* LPMA enabled */) {
49   // TODO(b/235631242): Implement this.
50 }
51 
init()52 bool ExynosDaemon::init() {
53   constexpr size_t kMaxTimeSyncRetries = 5;
54   constexpr useconds_t kTimeSyncRetryDelayUs = 50000;  // 50 ms
55   bool success = false;
56   mNativeThreadHandle = 0;
57   siginterrupt(SIGINT, true);
58   std::signal(SIGINT, signalHandler);
59   if ((mCommsReadFd = open(kCommsDeviceFilename, O_RDONLY | O_CLOEXEC)) < 0) {
60     LOGE("Read FD open failed: %s", strerror(errno));
61   } else if ((mCommsWriteFd =
62                   open(kCommsDeviceFilename, O_WRONLY | O_CLOEXEC)) < 0) {
63     LOGE("Write FD open failed: %s", strerror(errno));
64   } else {
65     mProcessThreadRunning = true;
66     mIncomingMsgProcessThread =
67         std::thread([&] { this->processIncomingMsgs(); });
68     mNativeThreadHandle = mIncomingMsgProcessThread.native_handle();
69 
70     if (!sendTimeSyncWithRetry(kMaxTimeSyncRetries, kTimeSyncRetryDelayUs,
71                                true /* logOnError */)) {
72       LOGE("Failed to send initial time sync message");
73     } else {
74       loadPreloadedNanoapps();
75       success = true;
76       LOGD("CHRE daemon initialized successfully");
77     }
78   }
79   return success;
80 }
81 
deinit()82 void ExynosDaemon::deinit() {
83   stopMsgProcessingThread();
84 
85   close(mCommsWriteFd);
86   mCommsWriteFd = kInvalidFd;
87 
88   close(mCommsReadFd);
89   mCommsReadFd = kInvalidFd;
90 }
91 
run()92 void ExynosDaemon::run() {
93   constexpr char kChreSocketName[] = "chre";
94   auto serverCb = [&](uint16_t clientId, void *data, size_t len) {
95     sendMessageToChre(clientId, data, len);
96   };
97 
98   mServer.run(kChreSocketName, true /* allowSocketCreation */, serverCb);
99 }
100 
stopMsgProcessingThread()101 void ExynosDaemon::stopMsgProcessingThread() {
102   if (mProcessThreadRunning) {
103     mProcessThreadRunning = false;
104     pthread_kill(mNativeThreadHandle, SIGINT);
105     if (mIncomingMsgProcessThread.joinable()) {
106       mIncomingMsgProcessThread.join();
107     }
108   }
109 }
110 
processIncomingMsgs()111 void ExynosDaemon::processIncomingMsgs() {
112   std::array<uint8_t, kIpcMsgSizeMax> message;
113   int epollFd = createEpollFd(mCommsReadFd);
114 
115   while (mProcessThreadRunning) {
116     struct epoll_event retEvent;
117     int nEvents = epoll_wait(epollFd, &retEvent, 1 /* maxEvents */,
118                              -1 /* infinite timeout */);
119     if (nEvents < 0) {
120       // epoll_wait will get interrupted if the CHRE daemon is shutting down,
121       // check this condition before logging an error.
122       if (mProcessThreadRunning) {
123         LOGE("Epolling failed: %s", strerror(errno));
124       }
125     } else if (nEvents == 0) {
126       LOGW("Epoll returned with 0 FDs ready despite no timeout (errno: %s)",
127            strerror(errno));
128     } else {
129       int bytesRead = read(mCommsReadFd, message.data(), message.size());
130       if (bytesRead < 0) {
131         LOGE("Failed to read from fd: %s", strerror(errno));
132       } else if (bytesRead == 0) {
133         LOGE("Read 0 bytes from fd");
134       } else {
135         onMessageReceived(message.data(), bytesRead);
136       }
137     }
138   }
139 }
140 
doSendMessage(void * data,size_t length)141 bool ExynosDaemon::doSendMessage(void *data, size_t length) {
142   bool success = false;
143   if (length > kIpcMsgSizeMax) {
144     LOGE("Msg size %zu larger than max msg size %zu", length, kIpcMsgSizeMax);
145   } else {
146     ssize_t rv = write(mCommsWriteFd, data, length);
147 
148     if (rv < 0) {
149       LOGE("Failed to send message: %s", strerror(errno));
150     } else if (rv != length) {
151       LOGW("Msg send data loss: %zd of %zu bytes were written", rv, length);
152     } else {
153       success = true;
154     }
155   }
156   return success;
157 }
158 
getTimeOffset(bool * success)159 int64_t ExynosDaemon::getTimeOffset(bool *success) {
160   // TODO(b/235631242): Implement this.
161   *success = false;
162   return 0;
163 }
164 
loadPreloadedNanoapp(const std::string & directory,const std::string & name,uint32_t transactionId)165 void ExynosDaemon::loadPreloadedNanoapp(const std::string &directory,
166                                         const std::string &name,
167                                         uint32_t transactionId) {
168   std::vector<uint8_t> headerBuffer;
169   std::vector<uint8_t> nanoappBuffer;
170 
171   std::string headerFilename = directory + "/" + name + ".napp_header";
172   std::string nanoappFilename = directory + "/" + name + ".so";
173 
174   if (readFileContents(headerFilename.c_str(), headerBuffer) &&
175       readFileContents(nanoappFilename.c_str(), nanoappBuffer) &&
176       !loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) {
177     LOGE("Failed to load nanoapp: '%s'", name.c_str());
178   }
179 }
180 
loadNanoapp(const std::vector<uint8_t> & header,const std::vector<uint8_t> & nanoapp,uint32_t transactionId)181 bool ExynosDaemon::loadNanoapp(const std::vector<uint8_t> &header,
182                                const std::vector<uint8_t> &nanoapp,
183                                uint32_t transactionId) {
184   // This struct comes from build/build_template.mk and must not be modified.
185   // Refer to that file for more details.
186   struct NanoAppBinaryHeader {
187     uint32_t headerVersion;
188     uint32_t magic;
189     uint64_t appId;
190     uint32_t appVersion;
191     uint32_t flags;
192     uint64_t hwHubType;
193     uint8_t targetChreApiMajorVersion;
194     uint8_t targetChreApiMinorVersion;
195     uint8_t reserved[6];
196   } __attribute__((packed));
197 
198   bool success = false;
199   if (header.size() != sizeof(NanoAppBinaryHeader)) {
200     LOGE("Header size mismatch");
201   } else {
202     // The header blob contains the struct above.
203     const auto *appHeader =
204         reinterpret_cast<const NanoAppBinaryHeader *>(header.data());
205 
206     // Build the target API version from major and minor.
207     uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
208                                 (appHeader->targetChreApiMinorVersion << 16);
209 
210     success = sendFragmentedNanoappLoad(
211         appHeader->appId, appHeader->appVersion, appHeader->flags,
212         targetApiVersion, nanoapp.data(), nanoapp.size(), transactionId);
213   }
214 
215   return success;
216 }
217 
sendFragmentedNanoappLoad(uint64_t appId,uint32_t appVersion,uint32_t appFlags,uint32_t appTargetApiVersion,const uint8_t * appBinary,size_t appSize,uint32_t transactionId)218 bool ExynosDaemon::sendFragmentedNanoappLoad(
219     uint64_t appId, uint32_t appVersion, uint32_t appFlags,
220     uint32_t appTargetApiVersion, const uint8_t *appBinary, size_t appSize,
221     uint32_t transactionId) {
222   std::vector<uint8_t> binary(appSize);
223   std::copy(appBinary, appBinary + appSize, binary.begin());
224 
225   FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
226                                         appFlags, appTargetApiVersion, binary);
227 
228   bool success = true;
229 
230   while (success && !transaction.isComplete()) {
231     // Pad the builder to avoid allocation churn.
232     const auto &fragment = transaction.getNextRequest();
233     flatbuffers::FlatBufferBuilder builder(fragment.binary.size() + 128);
234     HostProtocolHost::encodeFragmentedLoadNanoappRequest(
235         builder, fragment, true /* respondBeforeStart */);
236     success = sendFragmentAndWaitOnResponse(transactionId, builder,
237                                             fragment.fragmentId, appId);
238   }
239 
240   return success;
241 }
242 
sendFragmentAndWaitOnResponse(uint32_t transactionId,flatbuffers::FlatBufferBuilder & builder,uint32_t fragmentId,uint64_t appId)243 bool ExynosDaemon::sendFragmentAndWaitOnResponse(
244     uint32_t transactionId, flatbuffers::FlatBufferBuilder &builder,
245     uint32_t fragmentId, uint64_t appId) {
246   bool success = true;
247   std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
248 
249   mPreloadedNanoappPendingTransaction = {
250       .transactionId = transactionId,
251       .fragmentId = fragmentId,
252       .nanoappId = appId,
253   };
254   mPreloadedNanoappPending = sendMessageToChre(
255       kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
256   if (!mPreloadedNanoappPending) {
257     LOGE("Failed to send nanoapp fragment");
258     success = false;
259   } else {
260     std::chrono::seconds timeout(2);
261     bool signaled = mPreloadedNanoappsCond.wait_for(
262         lock, timeout, [this] { return !mPreloadedNanoappPending; });
263 
264     if (!signaled) {
265       LOGE("Nanoapp fragment load timed out");
266       success = false;
267     }
268   }
269   return success;
270 }
271 
handleDaemonMessage(const uint8_t * message)272 void ExynosDaemon::handleDaemonMessage(const uint8_t *message) {
273   std::unique_ptr<fbs::MessageContainerT> container =
274       fbs::UnPackMessageContainer(message);
275   if (container->message.type != fbs::ChreMessage::LoadNanoappResponse) {
276     LOGE("Invalid message from CHRE directed to daemon");
277   } else {
278     const auto *response = container->message.AsLoadNanoappResponse();
279     std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
280 
281     if (!mPreloadedNanoappPending) {
282       LOGE("Received nanoapp load response with no pending load");
283     } else if (mPreloadedNanoappPendingTransaction.transactionId !=
284                response->transaction_id) {
285       LOGE("Received nanoapp load response with invalid transaction id");
286     } else if (mPreloadedNanoappPendingTransaction.fragmentId !=
287                response->fragment_id) {
288       LOGE("Received nanoapp load response with invalid fragment id");
289     } else if (!response->success) {
290 #ifdef CHRE_DAEMON_METRIC_ENABLED
291       std::vector<VendorAtomValue> values(3);
292       values[0].set<VendorAtomValue::longValue>(
293           mPreloadedNanoappPendingTransaction.nanoappId);
294       values[1].set<VendorAtomValue::intValue>(
295           Atoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED);
296       values[2].set<VendorAtomValue::intValue>(
297           Atoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC);
298       const VendorAtom atom{
299           .atomId = Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED,
300           .values{std::move(values)},
301       };
302       ChreDaemonBase::reportMetric(atom);
303 #endif  // CHRE_DAEMON_METRIC_ENABLED
304 
305     } else {
306       mPreloadedNanoappPending = false;
307     }
308 
309     mPreloadedNanoappsCond.notify_all();
310   }
311 }
312 
313 }  // namespace chre
314 }  // namespace android
315