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 <cstdlib>
18 #include <fstream>
19
20 #include "chre_host/daemon_base.h"
21 #include "chre_host/log.h"
22 #include "chre_host/napp_header.h"
23
24 #include <json/json.h>
25
26 // Aliased for consistency with the way these symbols are referenced in
27 // CHRE-side code
28 namespace fbs = ::chre::fbs;
29
30 namespace android {
31 namespace chre {
32
loadPreloadedNanoapps()33 void ChreDaemonBase::loadPreloadedNanoapps() {
34 constexpr char kPreloadedNanoappsConfigPath[] =
35 "/vendor/etc/chre/preloaded_nanoapps.json";
36 std::ifstream configFileStream(kPreloadedNanoappsConfigPath);
37
38 Json::CharReaderBuilder builder;
39 Json::Value config;
40 if (!configFileStream) {
41 LOGE("Failed to open config file '%s': %d (%s)",
42 kPreloadedNanoappsConfigPath, errno, strerror(errno));
43 } else if (!Json::parseFromStream(builder, configFileStream, &config,
44 /* errorMessage = */ nullptr)) {
45 LOGE("Failed to parse nanoapp config file");
46 } else if (!config.isMember("nanoapps") || !config.isMember("source_dir")) {
47 LOGE("Malformed preloaded nanoapps config");
48 } else {
49 const Json::Value &directory = config["source_dir"];
50 for (Json::ArrayIndex i = 0; i < config["nanoapps"].size(); i++) {
51 const Json::Value &nanoapp = config["nanoapps"][i];
52 loadPreloadedNanoapp(directory.asString(), nanoapp.asString(),
53 static_cast<uint32_t>(i));
54 }
55 }
56 }
57
loadPreloadedNanoapp(const std::string & directory,const std::string & name,uint32_t transactionId)58 void ChreDaemonBase::loadPreloadedNanoapp(const std::string &directory,
59 const std::string &name,
60 uint32_t transactionId) {
61 std::vector<uint8_t> headerBuffer;
62
63 std::string headerFile = directory + "/" + name + ".napp_header";
64
65 // Only create the nanoapp filename as the CHRE framework will load from
66 // within the directory its own binary resides in.
67 std::string nanoappFilename = name + ".so";
68
69 if (readFileContents(headerFile.c_str(), &headerBuffer) &&
70 !loadNanoapp(headerBuffer, nanoappFilename, transactionId)) {
71 LOGE("Failed to load nanoapp: '%s'", name.c_str());
72 }
73 }
74
loadNanoapp(const std::vector<uint8_t> & header,const std::string & nanoappName,uint32_t transactionId)75 bool ChreDaemonBase::loadNanoapp(const std::vector<uint8_t> &header,
76 const std::string &nanoappName,
77 uint32_t transactionId) {
78 bool success = false;
79 if (header.size() != sizeof(NanoAppBinaryHeader)) {
80 LOGE("Header size mismatch");
81 } else {
82 // The header blob contains the struct above.
83 const auto *appHeader =
84 reinterpret_cast<const NanoAppBinaryHeader *>(header.data());
85
86 // Build the target API version from major and minor.
87 uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
88 (appHeader->targetChreApiMinorVersion << 16);
89
90 success = sendNanoappLoad(appHeader->appId, appHeader->appVersion,
91 targetApiVersion, nanoappName, transactionId);
92 }
93
94 return success;
95 }
96
sendNanoappLoad(uint64_t appId,uint32_t appVersion,uint32_t appTargetApiVersion,const std::string & appBinaryName,uint32_t transactionId)97 bool ChreDaemonBase::sendNanoappLoad(uint64_t appId, uint32_t appVersion,
98 uint32_t appTargetApiVersion,
99 const std::string &appBinaryName,
100 uint32_t transactionId) {
101 flatbuffers::FlatBufferBuilder builder;
102 HostProtocolHost::encodeLoadNanoappRequestForFile(
103 builder, transactionId, appId, appVersion, appTargetApiVersion,
104 appBinaryName.c_str());
105
106 bool success = sendMessageToChre(
107 kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
108
109 if (!success) {
110 LOGE("Failed to send nanoapp filename.");
111 } else {
112 mPreloadedNanoappPendingTransactionIds.push(transactionId);
113 }
114
115 return success;
116 }
117
sendTimeSync(bool logOnError)118 bool ChreDaemonBase::sendTimeSync(bool logOnError) {
119 bool success = false;
120 int64_t timeOffset = getTimeOffset(&success);
121
122 if (success) {
123 flatbuffers::FlatBufferBuilder builder(64);
124 HostProtocolHost::encodeTimeSyncMessage(builder, timeOffset);
125 success = sendMessageToChre(kHostClientIdDaemon, builder.GetBufferPointer(),
126 builder.GetSize());
127
128 if (!success && logOnError) {
129 LOGE("Failed to deliver time sync message from host to CHRE");
130 }
131 }
132
133 return success;
134 }
135
sendTimeSyncWithRetry(size_t numRetries,useconds_t retryDelayUs,bool logOnError)136 bool ChreDaemonBase::sendTimeSyncWithRetry(size_t numRetries,
137 useconds_t retryDelayUs,
138 bool logOnError) {
139 bool success = false;
140 while (!success && (numRetries-- != 0)) {
141 success = sendTimeSync(logOnError);
142 if (!success) {
143 usleep(retryDelayUs);
144 }
145 }
146 return success;
147 }
148
sendMessageToChre(uint16_t clientId,void * data,size_t length)149 bool ChreDaemonBase::sendMessageToChre(uint16_t clientId, void *data,
150 size_t length) {
151 bool success = false;
152 if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) {
153 LOGE("Couldn't set host client ID in message container!");
154 } else {
155 LOGV("Delivering message from host (size %zu)", length);
156 getLogger()->dump(static_cast<const uint8_t *>(data), length);
157 success = doSendMessage(data, length);
158 }
159
160 return success;
161 }
162
onMessageReceived(const unsigned char * messageBuffer,size_t messageLen)163 void ChreDaemonBase::onMessageReceived(const unsigned char *messageBuffer,
164 size_t messageLen) {
165 getLogger()->dump(messageBuffer, messageLen);
166
167 uint16_t hostClientId;
168 fbs::ChreMessage messageType;
169 if (!HostProtocolHost::extractHostClientIdAndType(
170 messageBuffer, messageLen, &hostClientId, &messageType)) {
171 LOGW("Failed to extract host client ID from message - sending broadcast");
172 hostClientId = ::chre::kHostClientIdUnspecified;
173 }
174
175 if (messageType == fbs::ChreMessage::LogMessage) {
176 std::unique_ptr<fbs::MessageContainerT> container =
177 fbs::UnPackMessageContainer(messageBuffer);
178 const auto *logMessage = container->message.AsLogMessage();
179 const std::vector<int8_t> &logData = logMessage->buffer;
180
181 getLogger()->log(reinterpret_cast<const uint8_t *>(logData.data()),
182 logData.size());
183 } else if (messageType == fbs::ChreMessage::LogMessageV2) {
184 std::unique_ptr<fbs::MessageContainerT> container =
185 fbs::UnPackMessageContainer(messageBuffer);
186 const auto *logMessage = container->message.AsLogMessageV2();
187 const std::vector<int8_t> &logData = logMessage->buffer;
188 uint32_t numLogsDropped = logMessage->num_logs_dropped;
189
190 getLogger()->logV2(reinterpret_cast<const uint8_t *>(logData.data()),
191 logData.size(), numLogsDropped);
192 } else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
193 sendTimeSync(true /* logOnError */);
194 } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
195 configureLpma(true /* enabled */);
196 } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
197 configureLpma(false /* enabled */);
198 } else if (hostClientId == kHostClientIdDaemon) {
199 handleDaemonMessage(messageBuffer);
200 } else if (hostClientId == ::chre::kHostClientIdUnspecified) {
201 mServer.sendToAllClients(messageBuffer, static_cast<size_t>(messageLen));
202 } else {
203 mServer.sendToClientById(messageBuffer, static_cast<size_t>(messageLen),
204 hostClientId);
205 }
206 }
207
readFileContents(const char * filename,std::vector<uint8_t> * buffer)208 bool ChreDaemonBase::readFileContents(const char *filename,
209 std::vector<uint8_t> *buffer) {
210 bool success = false;
211 std::ifstream file(filename, std::ios::binary | std::ios::ate);
212 if (!file) {
213 LOGE("Couldn't open file '%s': %d (%s)", filename, errno, strerror(errno));
214 } else {
215 ssize_t size = file.tellg();
216 file.seekg(0, std::ios::beg);
217
218 buffer->resize(size);
219 if (!file.read(reinterpret_cast<char *>(buffer->data()), size)) {
220 LOGE("Couldn't read from file '%s': %d (%s)", filename, errno,
221 strerror(errno));
222 } else {
223 success = true;
224 }
225 }
226
227 return success;
228 }
229
handleDaemonMessage(const uint8_t * message)230 void ChreDaemonBase::handleDaemonMessage(const uint8_t *message) {
231 std::unique_ptr<fbs::MessageContainerT> container =
232 fbs::UnPackMessageContainer(message);
233 if (container->message.type != fbs::ChreMessage::LoadNanoappResponse) {
234 LOGE("Invalid message from CHRE directed to daemon");
235 } else {
236 const auto *response = container->message.AsLoadNanoappResponse();
237 if (mPreloadedNanoappPendingTransactionIds.empty()) {
238 LOGE("Received nanoapp load response with no pending load");
239 } else if (mPreloadedNanoappPendingTransactionIds.front() !=
240 response->transaction_id) {
241 LOGE("Received nanoapp load response with ID %" PRIu32
242 " expected transaction id %" PRIu32,
243 response->transaction_id,
244 mPreloadedNanoappPendingTransactionIds.front());
245 } else {
246 if (!response->success) {
247 LOGE("Received unsuccessful nanoapp load response with ID %" PRIu32,
248 mPreloadedNanoappPendingTransactionIds.front());
249 }
250 mPreloadedNanoappPendingTransactionIds.pop();
251 }
252 }
253 }
254
255 } // namespace chre
256 } // namespace android
257