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 "chre_host/preloaded_nanoapp_loader.h"
18 #include <chre_host/host_protocol_host.h>
19 #include <fstream>
20 #include "chre_host/config_util.h"
21 #include "chre_host/file_stream.h"
22 #include "chre_host/fragmented_load_transaction.h"
23 #include "chre_host/log.h"
24 #include "chre_host/nanoapp_load_listener.h"
25 #include "hal_client_id.h"
26
27 namespace android::chre {
28
29 namespace {
30
31 /** Timeout value of waiting for the response of a loading fragment. */
32 constexpr auto kTimeoutInMs = std::chrono::milliseconds(2000);
33
34 using ::android::chre::readFileContents;
35 using ::android::chre::Atoms::ChreHalNanoappLoadFailed;
36 using ::android::hardware::contexthub::common::implementation::kHalId;
37
getNanoappHeaderFromFile(const char * headerFileName,std::vector<uint8_t> & headerBuffer)38 bool getNanoappHeaderFromFile(const char *headerFileName,
39 std::vector<uint8_t> &headerBuffer) {
40 if (!readFileContents(headerFileName, headerBuffer)) {
41 LOGE("Failed to read header file for nanoapp %s", headerFileName);
42 return false;
43 }
44 if (headerBuffer.size() != sizeof(NanoAppBinaryHeader)) {
45 LOGE("Nanoapp binary's header size is incorrect");
46 return false;
47 }
48 return true;
49 }
50
shouldSkipNanoapp(std::optional<const std::vector<uint64_t>> nanoappIds,uint64_t theAppId)51 inline bool shouldSkipNanoapp(
52 std::optional<const std::vector<uint64_t>> nanoappIds, uint64_t theAppId) {
53 return nanoappIds.has_value() &&
54 std::find(nanoappIds->begin(), nanoappIds->end(), theAppId) !=
55 nanoappIds->end();
56 }
57 } // namespace
58
getPreloadedNanoappIds(std::vector<uint64_t> & out_preloadedNanoappIds)59 void PreloadedNanoappLoader::getPreloadedNanoappIds(
60 std::vector<uint64_t> &out_preloadedNanoappIds) {
61 std::vector<std::string> nanoappNames;
62 std::string directory;
63 out_preloadedNanoappIds.clear();
64 if (!getPreloadedNanoappsFromConfigFile(mConfigPath, directory,
65 nanoappNames)) {
66 LOGE("Failed to parse preloaded nanoapps config file");
67 }
68 for (const std::string &nanoappName : nanoappNames) {
69 std::string headerFileName = directory + "/" + nanoappName + ".napp_header";
70 std::vector<uint8_t> headerBuffer;
71 if (!getNanoappHeaderFromFile(headerFileName.c_str(), headerBuffer)) {
72 LOGE("Failed to parse the nanoapp header for %s", headerFileName.c_str());
73 continue;
74 }
75 auto header =
76 reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data());
77 out_preloadedNanoappIds.emplace_back(header->appId);
78 }
79 }
80
loadPreloadedNanoapps(const std::optional<const std::vector<uint64_t>> & skippedNanoappIds)81 int PreloadedNanoappLoader::loadPreloadedNanoapps(
82 const std::optional<const std::vector<uint64_t>> &skippedNanoappIds) {
83 std::string directory;
84 std::vector<std::string> nanoapps;
85 int numOfNanoappsLoaded = 0;
86 if (!getPreloadedNanoappsFromConfigFile(mConfigPath, directory, nanoapps)) {
87 LOGE("Failed to load any preloaded nanoapp");
88 return numOfNanoappsLoaded;
89 }
90 if (mIsPreloadingOngoing.exchange(true)) {
91 LOGE("Preloading is ongoing. A new request shouldn't happen.");
92 return numOfNanoappsLoaded;
93 }
94
95 for (uint32_t i = 0; i < nanoapps.size(); ++i) {
96 std::string headerFilename = directory + "/" + nanoapps[i] + ".napp_header";
97 std::string nanoappFilename = directory + "/" + nanoapps[i] + ".so";
98 // parse the header
99 std::vector<uint8_t> headerBuffer;
100 if (!getNanoappHeaderFromFile(headerFilename.c_str(), headerBuffer)) {
101 LOGE("Failed to parse the nanoapp header for %s",
102 nanoappFilename.c_str());
103 continue;
104 }
105 const auto header =
106 reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data());
107 // check if the app should be skipped
108 if (shouldSkipNanoapp(skippedNanoappIds, header->appId)) {
109 LOGI("Loading of %s is skipped.", nanoappFilename.c_str());
110 continue;
111 }
112 // load the binary
113 if (loadNanoapp(header, nanoappFilename, /* transactionId= */ i)) {
114 numOfNanoappsLoaded++;
115 } else {
116 LOGE("Failed to load nanoapp 0x%" PRIx64 " in preloaded nanoapp loader",
117 header->appId);
118 if (mNanoappLoadListener != nullptr) {
119 mNanoappLoadListener->onNanoappLoadFailed(header->appId);
120 }
121 }
122 }
123 mIsPreloadingOngoing.store(false);
124 return numOfNanoappsLoaded;
125 }
126
loadNanoapp(const NanoAppBinaryHeader * appHeader,const std::string & nanoappFileName,uint32_t transactionId)127 bool PreloadedNanoappLoader::loadNanoapp(const NanoAppBinaryHeader *appHeader,
128 const std::string &nanoappFileName,
129 uint32_t transactionId) {
130 // parse the binary
131 auto nanoappBuffer = std::make_shared<std::vector<uint8_t>>();
132 if (!readFileContents(nanoappFileName.c_str(), *nanoappBuffer)) {
133 LOGE("Unable to read %s.", nanoappFileName.c_str());
134 return false;
135 }
136 if (mNanoappLoadListener != nullptr) {
137 mNanoappLoadListener->onNanoappLoadStarted(appHeader->appId, nanoappBuffer);
138 }
139 // Build the target API version from major and minor.
140 uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
141 (appHeader->targetChreApiMinorVersion << 16);
142 bool success = sendFragmentedLoadAndWaitForEachResponse(
143 appHeader->appId, appHeader->appVersion, appHeader->flags,
144 targetApiVersion, nanoappBuffer->data(), nanoappBuffer->size(),
145 transactionId);
146 mEventLogger.logNanoappLoad(appHeader->appId, nanoappBuffer->size(),
147 appHeader->appVersion, success);
148 return success;
149 }
150
sendFragmentedLoadAndWaitForEachResponse(uint64_t appId,uint32_t appVersion,uint32_t appFlags,uint32_t appTargetApiVersion,const uint8_t * appBinary,size_t appSize,uint32_t transactionId)151 bool PreloadedNanoappLoader::sendFragmentedLoadAndWaitForEachResponse(
152 uint64_t appId, uint32_t appVersion, uint32_t appFlags,
153 uint32_t appTargetApiVersion, const uint8_t *appBinary, size_t appSize,
154 uint32_t transactionId) {
155 std::vector<uint8_t> binary(appSize);
156 std::copy(appBinary, appBinary + appSize, binary.begin());
157
158 FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
159 appFlags, appTargetApiVersion, binary);
160 while (!transaction.isComplete()) {
161 auto nextRequest = transaction.getNextRequest();
162 auto future = sendFragmentedLoadRequest(nextRequest);
163 if (!waitAndVerifyFuture(future, nextRequest)) {
164 return false;
165 }
166 }
167 return true;
168 }
169
waitAndVerifyFuture(std::future<bool> & future,const FragmentedLoadRequest & request)170 bool PreloadedNanoappLoader::waitAndVerifyFuture(
171 std::future<bool> &future, const FragmentedLoadRequest &request) {
172 bool success = false;
173 auto failureReason =
174 ChreHalNanoappLoadFailed::Reason::REASON_CONNECTION_ERROR;
175 if (!future.valid()) {
176 LOGE("Failed to send out the fragmented load fragment");
177 } else if (future.wait_for(kTimeoutInMs) != std::future_status::ready) {
178 LOGE(
179 "Waiting for response of fragment %zu transaction %d times out "
180 "after %lld ms",
181 request.fragmentId, request.transactionId, kTimeoutInMs.count());
182 } else if (!future.get()) {
183 LOGE(
184 "Received a failure result for loading fragment %zu of "
185 "transaction %d",
186 request.fragmentId, request.transactionId);
187 failureReason = ChreHalNanoappLoadFailed::Reason::REASON_ERROR_GENERIC;
188 } else {
189 success = true;
190 }
191
192 if (!success && mMetricsReporter != nullptr) {
193 mMetricsReporter->logNanoappLoadFailed(
194 request.appId, ChreHalNanoappLoadFailed::Type::TYPE_PRELOADED,
195 failureReason);
196 }
197 return success;
198 }
199
verifyFragmentLoadResponse(const::chre::fbs::LoadNanoappResponseT & response) const200 bool PreloadedNanoappLoader::verifyFragmentLoadResponse(
201 const ::chre::fbs::LoadNanoappResponseT &response) const {
202 if (!response.success) {
203 LOGE("Loading nanoapp binary fragment %d of transaction %u failed.",
204 response.fragment_id, response.transaction_id);
205 return false;
206 }
207 if (mPreloadedNanoappPendingTransaction.fragmentId != response.fragment_id) {
208 LOGE(
209 "Fragmented load response with unexpected fragment id %u while "
210 "%zu is expected",
211 response.fragment_id, mPreloadedNanoappPendingTransaction.fragmentId);
212 return false;
213 }
214 return true;
215 }
216
onLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response,HalClientId clientId)217 bool PreloadedNanoappLoader::onLoadNanoappResponse(
218 const ::chre::fbs::LoadNanoappResponseT &response, HalClientId clientId) {
219 std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
220 if (clientId != kHalId || !mFragmentedLoadPromise.has_value()) {
221 LOGE(
222 "Received an unexpected preload nanoapp %s response for client %d "
223 "transaction %u fragment %u",
224 response.success ? "success" : "failure", clientId,
225 response.transaction_id, response.fragment_id);
226 return false;
227 }
228 if (mPreloadedNanoappPendingTransaction.transactionId !=
229 response.transaction_id) {
230 LOGE(
231 "Fragmented load response with transactionId %u but transactionId "
232 "%u is expected. Ignored.",
233 response.transaction_id,
234 mPreloadedNanoappPendingTransaction.transactionId);
235 return false;
236 }
237 // set value for the future instance.
238 mFragmentedLoadPromise->set_value(verifyFragmentLoadResponse(response));
239 // reset the promise as the value can only be retrieved once from it.
240 mFragmentedLoadPromise = std::nullopt;
241 return true;
242 }
243
sendFragmentedLoadRequest(::android::chre::FragmentedLoadRequest & request)244 std::future<bool> PreloadedNanoappLoader::sendFragmentedLoadRequest(
245 ::android::chre::FragmentedLoadRequest &request) {
246 flatbuffers::FlatBufferBuilder builder(request.binary.size() + 128);
247 // TODO(b/247124878): Confirm if respondBeforeStart can be set to true on all
248 // the devices.
249 HostProtocolHost::encodeFragmentedLoadNanoappRequest(
250 builder, request, /* respondBeforeStart= */ true);
251 HostProtocolHost::mutateHostClientId(builder.GetBufferPointer(),
252 builder.GetSize(), kHalId);
253
254 std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
255 if (!mConnection->sendMessage(builder.GetBufferPointer(),
256 builder.GetSize())) {
257 // Returns an invalid future to indicate the failure
258 return std::future<bool>{};
259 }
260 mPreloadedNanoappPendingTransaction = {
261 .transactionId = request.transactionId,
262 .fragmentId = request.fragmentId,
263 };
264 mFragmentedLoadPromise = std::make_optional<std::promise<bool>>();
265 return mFragmentedLoadPromise->get_future();
266 }
267 } // namespace android::chre