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