xref: /aosp_15_r20/system/chre/host/common/preloaded_nanoapp_loader.cc (revision 84e339476a462649f82315436d70fd732297a399)
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 
200 PreloadedNanoappLoader::ResponseVerificationResult
verifyFragmentLoadResponse(const::chre::fbs::LoadNanoappResponseT & response) const201 PreloadedNanoappLoader::verifyFragmentLoadResponse(
202     const ::chre::fbs::LoadNanoappResponseT &response) const {
203   // Allow seen fragment ids to be ignored to tolerate duplicated responses.
204   if (response.fragment_id >= 0 &&
205       response.fragment_id < mPendingTransaction.fragmentId) {
206     LOGW(
207         "Fragmented load response has a fragment id %u while %zu is expected. "
208         "Ignored",
209         response.fragment_id, mPendingTransaction.fragmentId);
210     return ResponseVerificationResult::IGNORED;
211   }
212 
213   // Future or negative fragment ids are not acceptable.
214   if (response.fragment_id != mPendingTransaction.fragmentId) {
215     LOGE(
216         "Fragmented load response with unexpected fragment id %u while "
217         "%zu is expected",
218         response.fragment_id, mPendingTransaction.fragmentId);
219     return ResponseVerificationResult::FAILURE;
220   }
221 
222   // Once fragment id is matched the result is taken.
223   if (!response.success) {
224     LOGE("Loading nanoapp binary fragment %d of transaction %u failed.",
225          response.fragment_id, response.transaction_id);
226     return ResponseVerificationResult::FAILURE;
227   }
228   return ResponseVerificationResult::SUCCESS;
229 }
230 
onLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response,HalClientId clientId)231 bool PreloadedNanoappLoader::onLoadNanoappResponse(
232     const ::chre::fbs::LoadNanoappResponseT &response, HalClientId clientId) {
233   std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
234   if (clientId != kHalId || !mFragmentedLoadPromise.has_value()) {
235     LOGE(
236         "Received an unexpected preload nanoapp %s response for client %d "
237         "transaction %u fragment %u",
238         response.success ? "success" : "failure", clientId,
239         response.transaction_id, response.fragment_id);
240     return false;
241   }
242   if (mPendingTransaction.transactionId != response.transaction_id) {
243     LOGE(
244         "Fragmented load response with transactionId %u but transactionId "
245         "%u is expected. Ignored.",
246         response.transaction_id, mPendingTransaction.transactionId);
247     return false;
248   }
249 
250   // set value for the future instance.
251   ResponseVerificationResult result = verifyFragmentLoadResponse(response);
252   if (result != ResponseVerificationResult::IGNORED) {
253     mFragmentedLoadPromise->set_value(result ==
254                                       ResponseVerificationResult::SUCCESS);
255     // reset the promise as the value can only be retrieved once from it.
256     mFragmentedLoadPromise = std::nullopt;
257   }
258 
259   return true;
260 }
261 
sendFragmentedLoadRequest(::android::chre::FragmentedLoadRequest & request)262 std::future<bool> PreloadedNanoappLoader::sendFragmentedLoadRequest(
263     ::android::chre::FragmentedLoadRequest &request) {
264   flatbuffers::FlatBufferBuilder builder(request.binary.size() + 128);
265   // TODO(b/247124878): Confirm if respondBeforeStart can be set to true on all
266   //  the devices.
267   HostProtocolHost::encodeFragmentedLoadNanoappRequest(
268       builder, request, /* respondBeforeStart= */ true);
269   HostProtocolHost::mutateHostClientId(builder.GetBufferPointer(),
270                                        builder.GetSize(), kHalId);
271 
272   std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
273   if (!mConnection->sendMessage(builder.GetBufferPointer(),
274                                 builder.GetSize())) {
275     // Returns an invalid future to indicate the failure
276     return std::future<bool>{};
277   }
278   mPendingTransaction = {
279       .transactionId = request.transactionId,
280       .fragmentId = request.fragmentId,
281   };
282   mFragmentedLoadPromise = std::make_optional<std::promise<bool>>();
283   return mFragmentedLoadPromise->get_future();
284 }
285 }  // namespace android::chre