Load preloaded nanoapps without using a buffer
In order to avoid allocating a temporary buffer,
load all preloaded nanoapps from their file stored
on disk. This allows us to prepare all preloaded nanoapps
before the PALs come online so we can start serving
requests (allowing boot to complete faster).
Bug: 120610282
Test: Loaded all updated components and verified nanoapps were
loaded and requests were served before the PAL comes online.
Also verified code download path still works by loading
geofence.napp w/ a modified gmscore.
Change-Id: I76845f934295552fe86d4eb2c5a1066101cdd84c
diff --git a/host/msm/daemon/chre_daemon.cc b/host/msm/daemon/chre_daemon.cc
index fddf819..74e7113 100644
--- a/host/msm/daemon/chre_daemon.cc
+++ b/host/msm/daemon/chre_daemon.cc
@@ -56,6 +56,7 @@
#include <fstream>
#include <string>
+#include <queue>
#include "chre/platform/slpi/fastrpc.h"
#include "chre_host/log.h"
@@ -130,18 +131,9 @@
//! clients after the server starts.
static const uint16_t kHostClientIdDaemon = UINT16_MAX;
-//! The mutex used to guard state between the nanoapp messaging thread
-//! and loading preloaded nanoapps.
-static pthread_mutex_t gPreloadedNanoappsMutex;
-
-//! The condition variable used to wait for a nanoapp to finish loading.
-static pthread_cond_t gPreloadedNanoappsCond;
-
-//! Set to true when a preloaded nanoapp is pending load.
-static bool gPreloadedNanoappPending;
-
-//! Set to the expected transaction ID for loading a nanoapp.
-static uint32_t gPreloadedNanoappPendingTransactionId;
+//! Contains a set of transaction IDs used to load the preloaded nanoapps.
+//! The IDs are stored in the order they are sent.
+static std::queue<uint32_t> gPreloadedNanoappPendingTransactionIds;
//! Set to true when we request a graceful shutdown of CHRE
static volatile bool chre_shutdown_requested = false;
@@ -540,79 +532,48 @@
}
/**
- * Loads a nanoapp using fragments.
+ * Loads a nanoapp by sending the nanoapp filename to the CHRE framework. This
+ * method will return after sending the request so no guarantee is made that
+ * the nanoapp is loaded until after the response is received.
*
* @param appId The ID of the nanoapp to load.
* @param appVersion The version of the nanoapp to load.
* @param appTargetApiVersion The version of the CHRE API that the app targets.
- * @param appBinary The application binary code.
- * @param appSize The size of the appBinary.
+ * @param appBinaryName The name of the binary as stored in the filesystem. This
+ * will be used to load the nanoapp into CHRE.
* @param transactionId The transaction ID to use when loading.
- * @return true if successful, false otherwise.
+ * @return true if a request was successfully sent, false otherwise.
*/
-static bool sendFragmentedNanoappLoad(
+static bool sendNanoappLoad(
uint64_t appId, uint32_t appVersion, uint32_t appTargetApiVersion,
- const uint8_t *appBinary, size_t appSize, uint32_t transactionId) {
- std::vector<uint8_t> binary(appSize);
- std::copy(appBinary, appBinary + appSize, binary.begin());
+ const std::string& appBinaryName, uint32_t transactionId) {
+ flatbuffers::FlatBufferBuilder builder;
+ HostProtocolHost::encodeLoadNanoappRequestForFile(
+ builder, transactionId, appId, appVersion, appTargetApiVersion,
+ appBinaryName.c_str());
- FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
- appTargetApiVersion, binary);
+ bool success = sendMessageToChre(
+ kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
- bool success = true;
- while (success && !transaction.isComplete()) {
- // Pad the builder to avoid allocation churn.
- const auto& fragment = transaction.getNextRequest();
- flatbuffers::FlatBufferBuilder builder(fragment.binary.size() + 128);
- HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, fragment);
-
- pthread_mutex_lock(&gPreloadedNanoappsMutex);
-
- gPreloadedNanoappPendingTransactionId = transactionId;
- gPreloadedNanoappPending = sendMessageToChre(
- kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
- if (!gPreloadedNanoappPending) {
- LOGE("Failed to send nanoapp fragment");
- success = false;
- } else {
- struct timespec timeout;
- int ret = clock_gettime(CLOCK_REALTIME, &timeout);
-
- // 60s timeout for fragments. Set high for busy first bootups where the
- // PALs can block CHRE initialization while other subsystems come up.
- timeout.tv_sec += 60;
- if (ret != 0) {
- LOG_ERROR("Nanoapp fragment get time failed", ret);
- success = false;
- } else {
- while (gPreloadedNanoappPending && ret == 0) {
- ret = pthread_cond_timedwait(&gPreloadedNanoappsCond,
- &gPreloadedNanoappsMutex, &timeout);
- }
-
- if (ret != 0) {
- LOG_ERROR("Nanoapp fragment load wait failed", ret);
- success = false;
- }
- }
- }
-
- pthread_mutex_unlock(&gPreloadedNanoappsMutex);
+ if (!success) {
+ LOGE("Failed to send nanoapp filename.");
+ } else {
+ gPreloadedNanoappPendingTransactionIds.push(transactionId);
}
return success;
}
/**
- * Sends a preloaded nanoapp to CHRE.
+ * Sends a preloaded nanoapp filename / metadata to CHRE.
*
* @param header The nanoapp header binary blob.
- * @param nanoapp The nanoapp binary blob.
+ * @param nanoappName The filename of the nanoapp to be loaded.
* @param transactionId The transaction ID to use when loading the app.
- * @return true if succssful, false otherwise.
+ * @return true if successful, false otherwise.
*/
static bool loadNanoapp(const std::vector<uint8_t>& header,
- const std::vector<uint8_t>& nanoapp,
+ const std::string& nanoappName,
uint32_t transactionId) {
// This struct comes from build/build_template.mk and must not be modified.
// Refer to that file for more details.
@@ -640,9 +601,8 @@
uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24)
| (appHeader->targetChreApiMinorVersion << 16);
- success = sendFragmentedNanoappLoad(appHeader->appId, appHeader->appVersion,
- targetApiVersion, nanoapp.data(),
- nanoapp.size(), transactionId);
+ success = sendNanoappLoad(appHeader->appId, appHeader->appVersion,
+ targetApiVersion, nanoappName, transactionId);
}
return success;
@@ -655,8 +615,8 @@
* @param buffer The buffer to load into.
* @return true if successful, false otherwise.
*/
-static bool loadPreloadedNanoappFile(const char *filename,
- std::vector<uint8_t> *buffer) {
+static bool readFileContents(const char *filename,
+ std::vector<uint8_t> *buffer) {
bool success = false;
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
@@ -678,22 +638,29 @@
}
/**
- * Loads a preloaded nanoapp given a filename to load from.
+ * Loads a preloaded nanoapp given a filename to load from. Allows the
+ * transaction to complete before the nanoapp starts so the server can start
+ * serving requests as soon as possible.
*
* @param name The filepath to load the nanoapp from.
* @param transactionId The transaction ID to use when loading the app.
*/
-static void loadPreloadedNanoapp(const char *name, uint32_t transactionId) {
+static void loadPreloadedNanoapp(const std::string& name,
+ uint32_t transactionId) {
std::vector<uint8_t> headerBuffer;
- std::vector<uint8_t> nanoappBuffer;
std::string headerFilename = std::string(name) + ".napp_header";
std::string nanoappFilename = std::string(name) + ".so";
- if (loadPreloadedNanoappFile(headerFilename.c_str(), &headerBuffer)
- && loadPreloadedNanoappFile(nanoappFilename.c_str(), &nanoappBuffer)
- && !loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) {
- LOGE("Failed to load nanoapp: '%s'", name);
+ // Only send the filename itself e.g activity.so since CHRE will load from
+ // the same directory its own binary resides in.
+ nanoappFilename = nanoappFilename.substr(
+ nanoappFilename.find_last_of("/\\") + 1);
+ if (nanoappFilename.empty()) {
+ LOGE("Failed to get the name of the nanoapp %s", name.c_str());
+ } else if (readFileContents(headerFilename.c_str(), &headerBuffer)
+ && !loadNanoapp(headerBuffer, nanoappFilename, transactionId)) {
+ LOGE("Failed to load nanoapp: '%s'", name.c_str());
}
}
@@ -709,31 +676,23 @@
* The napp_header and so files will both be loaded. All errors are logged.
*/
static void loadPreloadedNanoapps() {
- int ret;
- if ((ret = pthread_mutex_init(&gPreloadedNanoappsMutex, NULL)) != 0) {
- LOG_ERROR("Failed to initialize preloaded nanoapps mutex", ret);
- } else if ((ret = pthread_cond_init(&gPreloadedNanoappsCond, NULL)) != 0) {
- LOG_ERROR("Failed to initialize preloaded nanoapps condition variable",
- ret);
- } else {
- constexpr char kPreloadedNanoappsConfigPath[] =
- "/vendor/etc/chre/preloaded_nanoapps.json";
- std::ifstream configFileStream(kPreloadedNanoappsConfigPath);
+ constexpr char kPreloadedNanoappsConfigPath[] =
+ "/vendor/etc/chre/preloaded_nanoapps.json";
+ std::ifstream configFileStream(kPreloadedNanoappsConfigPath);
- Json::Reader reader;
- Json::Value config;
- if (!configFileStream) {
- LOGE("Failed to open config file '%s': %d (%s)",
- kPreloadedNanoappsConfigPath, errno, strerror(errno));
- } else if (!reader.parse(configFileStream, config)) {
- LOGE("Failed to parse nanoapp config file");
- } else if (!config.isMember("nanoapps")) {
- LOGE("Malformed preloaded nanoapps config");
- } else {
- for (Json::ArrayIndex i = 0; i < config["nanoapps"].size(); i++) {
- const Json::Value& nanoapp = config["nanoapps"][i];
- loadPreloadedNanoapp(nanoapp.asCString(), static_cast<uint32_t>(i));
- }
+ Json::Reader reader;
+ Json::Value config;
+ if (!configFileStream) {
+ LOGE("Failed to open config file '%s': %d (%s)",
+ kPreloadedNanoappsConfigPath, errno, strerror(errno));
+ } else if (!reader.parse(configFileStream, config)) {
+ LOGE("Failed to parse nanoapp config file");
+ } else if (!config.isMember("nanoapps")) {
+ LOGE("Malformed preloaded nanoapps config");
+ } else {
+ for (Json::ArrayIndex i = 0; i < config["nanoapps"].size(); i++) {
+ const Json::Value& nanoapp = config["nanoapps"][i];
+ loadPreloadedNanoapp(nanoapp.asString(), static_cast<uint32_t>(i));
}
}
}
@@ -751,18 +710,20 @@
LOGE("Invalid message from CHRE directed to daemon");
} else {
const auto *response = container->message.AsLoadNanoappResponse();
- pthread_mutex_lock(&gPreloadedNanoappsMutex);
- if (!gPreloadedNanoappPending) {
+ if (gPreloadedNanoappPendingTransactionIds.empty()) {
LOGE("Received nanoapp load response with no pending load");
- } else if (gPreloadedNanoappPendingTransactionId
+ } else if (gPreloadedNanoappPendingTransactionIds.front()
!= response->transaction_id) {
- LOGE("Received nanoapp load response with invalid transaction id");
+ LOGE("Received nanoapp load response with ID %" PRIu32
+ " expected transaction id %" PRIu32, response->transaction_id,
+ gPreloadedNanoappPendingTransactionIds.front());
} else {
- gPreloadedNanoappPending = false;
+ if (!response->success) {
+ LOGE("Received unsuccessful nanoapp load response with ID %" PRIu32,
+ gPreloadedNanoappPendingTransactionIds.front());
+ }
+ gPreloadedNanoappPendingTransactionIds.pop();
}
-
- pthread_mutex_unlock(&gPreloadedNanoappsMutex);
- pthread_cond_signal(&gPreloadedNanoappsCond);
}
}