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