• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "hal_client_id.h"
25 
26 namespace android::chre {
27 
28 using android::chre::readFileContents;
29 using android::hardware::contexthub::common::implementation::kHalId;
30 
getPreloadedNanoappIds(std::vector<uint64_t> & out_preloadedNanoappIds)31 void PreloadedNanoappLoader::getPreloadedNanoappIds(
32     std::vector<uint64_t> &out_preloadedNanoappIds) {
33   std::vector<std::string> nanoappNames;
34   std::string directory;
35   out_preloadedNanoappIds.clear();
36   bool success =
37       getPreloadedNanoappsFromConfigFile(mConfigPath, directory, nanoappNames);
38   if (!success) {
39     LOGE("Failed to parse preloaded nanoapps config file");
40   }
41   for (const std::string &nanoappName : nanoappNames) {
42     std::string headerFile = directory + "/" + nanoappName + ".napp_header";
43     std::vector<uint8_t> headerBuffer;
44     if (!readFileContents(headerFile.c_str(), headerBuffer)) {
45       LOGE("Cannot read header file: %s", headerFile.c_str());
46       continue;
47     }
48     if (headerBuffer.size() != sizeof(NanoAppBinaryHeader)) {
49       LOGE("Header size mismatch");
50       continue;
51     }
52     const auto *appHeader =
53         reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data());
54     out_preloadedNanoappIds.emplace_back(appHeader->appId);
55   }
56 }
57 
loadPreloadedNanoapps()58 void PreloadedNanoappLoader::loadPreloadedNanoapps() {
59   std::string directory;
60   std::vector<std::string> nanoapps;
61 
62   bool success =
63       getPreloadedNanoappsFromConfigFile(mConfigPath, directory, nanoapps);
64   if (!success) {
65     LOGE("Failed to load any preloaded nanoapp");
66   } else {
67     mIsPreloadingOngoing = true;
68     for (uint32_t i = 0; i < nanoapps.size(); ++i) {
69       loadPreloadedNanoapp(directory, nanoapps[i], i);
70     }
71     mIsPreloadingOngoing = false;
72   }
73 }
74 
loadPreloadedNanoapp(const std::string & directory,const std::string & name,uint32_t transactionId)75 void PreloadedNanoappLoader::loadPreloadedNanoapp(const std::string &directory,
76                                                   const std::string &name,
77                                                   uint32_t transactionId) {
78   std::vector<uint8_t> headerBuffer;
79   std::vector<uint8_t> nanoappBuffer;
80 
81   std::string headerFilename = directory + "/" + name + ".napp_header";
82   std::string nanoappFilename = directory + "/" + name + ".so";
83 
84   if (!readFileContents(headerFilename.c_str(), headerBuffer) ||
85       !readFileContents(nanoappFilename.c_str(), nanoappBuffer) ||
86       !loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) {
87     LOGE("Failed to load nanoapp: '%s'", name.c_str());
88   }
89 }
90 
loadNanoapp(const std::vector<uint8_t> & header,const std::vector<uint8_t> & nanoapp,uint32_t transactionId)91 bool PreloadedNanoappLoader::loadNanoapp(const std::vector<uint8_t> &header,
92                                          const std::vector<uint8_t> &nanoapp,
93                                          uint32_t transactionId) {
94   if (header.size() != sizeof(NanoAppBinaryHeader)) {
95     LOGE("Nanoapp binary's header size is incorrect");
96     return false;
97   }
98   const auto *appHeader =
99       reinterpret_cast<const NanoAppBinaryHeader *>(header.data());
100 
101   // Build the target API version from major and minor.
102   uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
103                               (appHeader->targetChreApiMinorVersion << 16);
104   return sendFragmentedLoadAndWaitForEachResponse(
105       appHeader->appId, appHeader->appVersion, appHeader->flags,
106       targetApiVersion, nanoapp.data(), nanoapp.size(), transactionId);
107 }
108 
sendFragmentedLoadAndWaitForEachResponse(uint64_t appId,uint32_t appVersion,uint32_t appFlags,uint32_t appTargetApiVersion,const uint8_t * appBinary,size_t appSize,uint32_t transactionId)109 bool PreloadedNanoappLoader::sendFragmentedLoadAndWaitForEachResponse(
110     uint64_t appId, uint32_t appVersion, uint32_t appFlags,
111     uint32_t appTargetApiVersion, const uint8_t *appBinary, size_t appSize,
112     uint32_t transactionId) {
113   std::vector<uint8_t> binary(appSize);
114   std::copy(appBinary, appBinary + appSize, binary.begin());
115 
116   FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
117                                         appFlags, appTargetApiVersion, binary);
118   while (!transaction.isComplete()) {
119     auto nextRequest = transaction.getNextRequest();
120     auto future = sendFragmentedLoadRequest(nextRequest);
121     if (!waitAndVerifyFuture(future, nextRequest)) {
122       return false;
123     }
124   }
125   return true;
126 }
127 
waitAndVerifyFuture(std::future<bool> & future,const FragmentedLoadRequest & request)128 bool PreloadedNanoappLoader::waitAndVerifyFuture(
129     std::future<bool> &future, const FragmentedLoadRequest &request) {
130   if (!future.valid()) {
131     LOGE("Failed to send out the fragmented load fragment");
132     return false;
133   }
134   if (future.wait_for(kTimeoutInMs) != std::future_status::ready) {
135     LOGE(
136         "Waiting for response of fragment %zu transaction %d times out "
137         "after %lld ms",
138         request.fragmentId, request.transactionId, kTimeoutInMs.count());
139     return false;
140   }
141   if (!future.get()) {
142     LOGE(
143         "Received a failure result for loading fragment %zu of "
144         "transaction %d",
145         request.fragmentId, request.transactionId);
146     return false;
147   }
148   return true;
149 }
150 
verifyFragmentLoadResponse(const::chre::fbs::LoadNanoappResponseT & response) const151 bool PreloadedNanoappLoader::verifyFragmentLoadResponse(
152     const ::chre::fbs::LoadNanoappResponseT &response) const {
153   if (!response.success) {
154     LOGE("Loading nanoapp binary fragment %d of transaction %u failed.",
155          response.fragment_id, response.transaction_id);
156     // TODO(b/247124878): Report metrics.
157     return false;
158   }
159   if (mPreloadedNanoappPendingTransaction.transactionId !=
160       response.transaction_id) {
161     LOGE(
162         "Fragmented load response with transactionId %u but transactionId "
163         "%u is expected",
164         response.transaction_id,
165         mPreloadedNanoappPendingTransaction.transactionId);
166     return false;
167   }
168   if (mPreloadedNanoappPendingTransaction.fragmentId != response.fragment_id) {
169     LOGE(
170         "Fragmented load response with unexpected fragment id %u while "
171         "%zu is expected",
172         response.fragment_id, mPreloadedNanoappPendingTransaction.fragmentId);
173     return false;
174   }
175   return true;
176 }
177 
onLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response,HalClientId clientId)178 bool PreloadedNanoappLoader::onLoadNanoappResponse(
179     const ::chre::fbs::LoadNanoappResponseT &response, HalClientId clientId) {
180   std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
181   if (clientId != kHalId || !mFragmentedLoadPromise.has_value()) {
182     LOGE(
183         "Received an unexpected preload nanoapp %s response for client %d "
184         "transaction %u fragment %u",
185         response.success ? "success" : "failure", clientId,
186         response.transaction_id, response.fragment_id);
187     return false;
188   }
189   // set value for the future instance
190   mFragmentedLoadPromise->set_value(verifyFragmentLoadResponse(response));
191   // reset the promise as the value can only be retrieved once from it
192   mFragmentedLoadPromise = std::nullopt;
193   return true;
194 }
195 
sendFragmentedLoadRequest(::android::chre::FragmentedLoadRequest & request)196 std::future<bool> PreloadedNanoappLoader::sendFragmentedLoadRequest(
197     ::android::chre::FragmentedLoadRequest &request) {
198   flatbuffers::FlatBufferBuilder builder(request.binary.size() + 128);
199   // TODO(b/247124878): Confirm if respondBeforeStart can be set to true on all
200   //  the devices.
201   HostProtocolHost::encodeFragmentedLoadNanoappRequest(
202       builder, request, /* respondBeforeStart= */ true);
203   HostProtocolHost::mutateHostClientId(builder.GetBufferPointer(),
204                                        builder.GetSize(), kHalId);
205   std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
206   if (!mConnection->sendMessage(builder.GetBufferPointer(),
207                                 builder.GetSize())) {
208     // Returns an invalid future to indicate the failure
209     return std::future<bool>{};
210   }
211   mPreloadedNanoappPendingTransaction = {
212       .transactionId = request.transactionId,
213       .fragmentId = request.fragmentId,
214   };
215   mFragmentedLoadPromise = std::make_optional<std::promise<bool>>();
216   return mFragmentedLoadPromise->get_future();
217 }
218 }  // namespace android::chre