Update chre_aidl_hal_client to use callbacks
The hal client is also enhanced with following features:
- list all the preinstalled nanoapps on device with their header
properties including id, version, etc.
- list all the nanoapps loaded (system nanoapps excluded) with
their information.
- only supports hexdecimal appId
- use the preloaded nanoapps' binary files on device to load
Bug: 242177220
Test: run chre_aidl_hal_client manually
Change-Id: I05aafa52933f9e5157c16af86a23fe1e5522ce0a
diff --git a/Android.bp b/Android.bp
index cfc855f..32c0a0a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -125,7 +125,7 @@
"liblog",
"libutils",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: ["-Wall", "-Werror", "-fexceptions"],
}
cc_test {
diff --git a/host/common/chre_aidl_hal_client.cc b/host/common/chre_aidl_hal_client.cc
index b418f48..6e85195 100644
--- a/host/common/chre_aidl_hal_client.cc
+++ b/host/common/chre_aidl_hal_client.cc
@@ -14,101 +14,278 @@
* limitations under the License.
*/
-#include "chre_host/file_stream.h"
-#include "chre_host/log.h"
-
-#include <cinttypes>
-#include <string>
-#include <vector>
-
+#include <aidl/android/hardware/contexthub/BnContextHubCallback.h>
#include <aidl/android/hardware/contexthub/IContextHub.h>
#include <aidl/android/hardware/contexthub/NanoappBinary.h>
#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <dirent.h>
#include <utils/String16.h>
+#include <cinttypes>
+#include <filesystem>
+#include <fstream>
+#include <future>
+#include <map>
+#include <regex>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "chre_host/file_stream.h"
+#include "chre_host/napp_header.h"
+
+using aidl::android::hardware::contexthub::AsyncEventType;
+using aidl::android::hardware::contexthub::BnContextHubCallback;
+using aidl::android::hardware::contexthub::ContextHubMessage;
using aidl::android::hardware::contexthub::IContextHub;
using aidl::android::hardware::contexthub::NanoappBinary;
-using android::String16;
+using aidl::android::hardware::contexthub::NanoappInfo;
+using android::chre::NanoAppBinaryHeader;
using android::chre::readFileContents;
+using android::internal::ToString;
+using ndk::ScopedAStatus;
namespace {
-constexpr uint32_t kContextHubId = 0;
-constexpr int32_t kTransactionId = -1;
-
-bool loadNanoapp(const char *fileName, uint64_t nanoappId) {
- bool success = false;
- auto aidlServiceName = std::string() + IContextHub::descriptor + "/default";
- ndk::SpAIBinder binder(
- AServiceManager_waitForService(aidlServiceName.c_str()));
- if (binder.get() == nullptr) {
- LOGE("Could not find Context Hub HAL");
- } else {
- std::shared_ptr<IContextHub> contextHub = IContextHub::fromBinder(binder);
- contextHub->registerCallback(kContextHubId, nullptr);
- std::vector<uint8_t> soBuffer;
- if (!readFileContents(fileName, &soBuffer)) {
- LOGE("Failed to read contents of %s", fileName);
- } else {
- NanoappBinary binary;
- binary.nanoappId = nanoappId;
- binary.customBinary = soBuffer;
- success =
- contextHub->loadNanoapp(kContextHubId, binary, kTransactionId).isOk();
- LOGI("Load nanoapp 0x%" PRIx64 " success %d", nanoappId, success);
+class ContextHubCallback : public BnContextHubCallback {
+ public:
+ ScopedAStatus handleNanoappInfo(
+ const std::vector<NanoappInfo> &appInfo) override {
+ std::cout << appInfo.size() << " nanoapps loaded" << std::endl;
+ for (const NanoappInfo &app : appInfo) {
+ std::cout << "appId: 0x" << std::hex << app.nanoappId << std::dec << " {"
+ << "\n\tappVersion: " << app.nanoappVersion
+ << "\n\tenabled: " << app.enabled
+ << "\n\tpermissions: " << ToString(app.permissions)
+ << "\n\trpcServices: " << ToString(app.rpcServices) << "\n}"
+ << std::endl;
}
+ promise.set_value();
+ return ScopedAStatus::ok();
}
- return success;
+ ScopedAStatus handleContextHubMessage(
+ const ContextHubMessage & /*message*/,
+ const std::vector<std::string> & /*msgContentPerms*/) override {
+ promise.set_value();
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus handleContextHubAsyncEvent(AsyncEventType /*event*/) override {
+ promise.set_value();
+ return ScopedAStatus::ok();
+ }
+ // Called after loading/unloading a nanoapp.
+ ScopedAStatus handleTransactionResult(int32_t transactionId,
+ bool success) override {
+ std::cout << "[ContextHubCallback] Transaction " << transactionId << " is "
+ << (success ? "successful" : "failed") << std::endl;
+ promise.set_value();
+ return ScopedAStatus::ok();
+ }
+ std::promise<void> promise;
+};
+
+constexpr uint32_t kContextHubId = 0;
+constexpr int32_t kLoadTransactionId = 1;
+constexpr int32_t kUnloadTransactionId = -1;
+constexpr auto kTimeOutThreshold = std::chrono::seconds(5);
+constexpr char kUsage[] = R"(
+Usage: chre_aidl_hal_client COMMAND [ARGS]
+COMMAND ARGS...:
+ list <PATH_OF_NANOAPPS> - list all the nanoapps' header info in the path
+ load <ABSOLUTE_PATH> - load the nanoapp specified by the absolute path
+ of the nanoapp. For example, load /path/to/awesome.so
+ query - show all loaded nanoapps (system apps excluded)
+ unload <HEX_NANOAPP_ID | ABSOLUTE_PATH>
+ - unload the nanoapp specified by either the nanoapp
+ id in hex format or the absolute path. For example,
+ unload 0x123def or unload /path/to/awesome.so
+)";
+
+void throwError(const std::string &message) {
+ throw std::system_error{std::error_code(), message};
}
-bool unloadNanoapp(uint64_t nanoappId) {
- bool success = false;
+std::shared_ptr<IContextHub> getContextHub(std::future<void> &callbackSignal) {
auto aidlServiceName = std::string() + IContextHub::descriptor + "/default";
ndk::SpAIBinder binder(
AServiceManager_waitForService(aidlServiceName.c_str()));
if (binder.get() == nullptr) {
- LOGE("Could not find Context Hub HAL");
- } else {
- std::shared_ptr<IContextHub> contextHub = IContextHub::fromBinder(binder);
- contextHub->registerCallback(kContextHubId, nullptr);
- success =
- contextHub->unloadNanoapp(kContextHubId, nanoappId, kTransactionId)
- .isOk();
- LOGI("Unload nanoapp 0x%" PRIx64 " success %d", nanoappId, success);
+ throwError("Could not find Context Hub HAL");
}
- return success;
+ std::shared_ptr<IContextHub> contextHub = IContextHub::fromBinder(binder);
+ std::shared_ptr<ContextHubCallback> callback =
+ ContextHubCallback::make<ContextHubCallback>();
+
+ if (!contextHub->registerCallback(kContextHubId, callback).isOk()) {
+ throwError("Failed to register the callback");
+ }
+ callbackSignal = callback->promise.get_future();
+ return contextHub;
}
-void printUsage() {
- LOGI(
- "\n"
- "Usage:\n"
- " chre_aidl_hal_client load <path to .so file> <nanoapp ID>\n"
- " chre_aidl_hal_client unload <nanoapp ID>");
+void printNanoappHeader(const NanoAppBinaryHeader &header) {
+ std::cout << " {"
+ << "\n\tappId: 0x" << std::hex << header.appId << std::dec
+ << "\n\tappVersion: " << header.appVersion
+ << "\n\tflags: " << header.flags << "\n\ttarget major version: "
+ << static_cast<int>(header.targetChreApiMajorVersion)
+ << "\n\ttarget minor version: "
+ << static_cast<int>(header.targetChreApiMinorVersion) << "\n}"
+ << std::endl;
+}
+
+void readNanoappHeaders(std::map<std::string, NanoAppBinaryHeader> &nanoapps,
+ const std::string &binaryPath) {
+ DIR *dir = opendir(binaryPath.c_str());
+ if (dir == nullptr) {
+ throwError("Unable to access the nanoapp path");
+ }
+ std::regex regex("(\\w+)\\.napp_header");
+ std::cmatch match;
+ for (struct dirent *entry; (entry = readdir(dir)) != nullptr;) {
+ if (!std::regex_match(entry->d_name, match, regex)) {
+ continue;
+ }
+ std::ifstream input(std::string(binaryPath) + "/" + entry->d_name,
+ std::ios::binary);
+ input.read(reinterpret_cast<char *>(&nanoapps[match[1]]),
+ sizeof(NanoAppBinaryHeader));
+ }
+ closedir(dir);
+}
+
+void verifyStatusAndSignal(const ScopedAStatus &status,
+ const std::future<void> &future_signal) {
+ if (!status.isOk()) {
+ throwError("API call fails with abnormal status");
+ }
+ auto future_status = future_signal.wait_for(kTimeOutThreshold);
+ if (future_status != std::future_status::ready) {
+ throwError("The callback function is not ready yet");
+ }
+}
+
+NanoAppBinaryHeader findHeaderByPath(const std::string &pathAndName) {
+ std::map<std::string, NanoAppBinaryHeader> nanoapps{};
+ // To match the file pattern of [path][name].so
+ std::regex pathNameRegex("(.*?)(\\w+)\\.so");
+ std::smatch smatch;
+ if (!std::regex_match(pathAndName, smatch, pathNameRegex)) {
+ throwError("Invalid nanoapp: " + pathAndName);
+ }
+ std::string fullPath = smatch[1];
+ std::string appName = smatch[2];
+ readNanoappHeaders(nanoapps, fullPath);
+ if (nanoapps.find(appName) == nanoapps.end()) {
+ throwError("Cannot find the nanoapp: " + appName);
+ }
+ return nanoapps[appName];
+}
+
+void loadNanoapp(const std::string &pathAndName) {
+ NanoAppBinaryHeader header = findHeaderByPath(pathAndName);
+ std::vector<uint8_t> soBuffer{};
+ if (!readFileContents(pathAndName.c_str(), &soBuffer)) {
+ throwError("Failed to open the content of " + pathAndName);
+ }
+ NanoappBinary binary;
+ binary.nanoappId = static_cast<int64_t>(header.appId);
+ binary.customBinary = soBuffer;
+ binary.flags = static_cast<int32_t>(header.flags);
+ binary.targetChreApiMajorVersion =
+ static_cast<int8_t>(header.targetChreApiMajorVersion);
+ binary.targetChreApiMinorVersion =
+ static_cast<int8_t>(header.targetChreApiMinorVersion);
+ binary.nanoappVersion = static_cast<int32_t>(header.appVersion);
+
+ std::future<void> callbackSignal;
+ auto status = getContextHub(callbackSignal)
+ ->loadNanoapp(kContextHubId, binary, kLoadTransactionId);
+ verifyStatusAndSignal(status, callbackSignal);
+}
+
+void unloadNanoapp(const std::string &appIdentifier) {
+ std::future<void> callbackSignal;
+ int64_t appId;
+ try {
+ // check if user provided the hex appId
+ appId = std::stoll(appIdentifier, nullptr, 16);
+ } catch (std::invalid_argument &e) {
+ // Treat the appIdentifier as the absolute path and name and try again
+ appId = static_cast<int64_t>(findHeaderByPath(appIdentifier).appId);
+ }
+ auto status = getContextHub(callbackSignal)
+ ->unloadNanoapp(kContextHubId, appId, kUnloadTransactionId);
+ verifyStatusAndSignal(status, callbackSignal);
+}
+
+void queryNanoapps() {
+ std::future<void> callbackSignal;
+ auto status = getContextHub(callbackSignal)->queryNanoapps(kContextHubId);
+ verifyStatusAndSignal(status, callbackSignal);
+}
+
+enum Command { list, load, query, unload, unsupported };
+
+struct CommandInfo {
+ Command cmd;
+ u_int8_t numofArgs; // including cmd;
+};
+
+Command parseCommand(const std::vector<std::string> &cmdLine) {
+ std::map<std::string, CommandInfo> commandMap{
+ {"list", {list, 2}},
+ {"load", {load, 2}},
+ {"query", {query, 1}},
+ {"unload", {unload, 2}},
+ };
+ if (cmdLine.empty() || commandMap.find(cmdLine[0]) == commandMap.end()) {
+ return unsupported;
+ }
+ auto cmdInfo = commandMap.at(cmdLine[0]);
+ return cmdLine.size() == cmdInfo.numofArgs ? cmdInfo.cmd : unsupported;
}
} // anonymous namespace
int main(int argc, char *argv[]) {
- int argi = 1;
- const std::string cmd{argi < argc ? argv[argi++] : ""};
+ // Start binder thread pool to enable callbacks.
+ ABinderProcess_startThreadPool();
- std::vector<std::string> args;
- while (argi < argc) {
- args.push_back(std::string(argv[argi++]));
+ std::vector<std::string> cmdLine{};
+ for (int i = 1; i < argc; i++) {
+ cmdLine.emplace_back(argv[i]);
}
-
- bool success = false;
- if (cmd == "load" && args.size() == 2) {
- std::string file = args[0];
- uint64_t nanoappId = strtoull(args[1].c_str(), NULL, 0);
- success = loadNanoapp(file.c_str(), nanoappId);
- } else if (cmd == "unload" && args.size() == 1) {
- uint64_t nanoappId = strtoull(args[0].c_str(), NULL, 0);
- success = unloadNanoapp(nanoappId);
- } else {
- printUsage();
+ try {
+ switch (parseCommand(cmdLine)) {
+ case list: {
+ std::map<std::string, NanoAppBinaryHeader> nanoapps{};
+ readNanoappHeaders(nanoapps, cmdLine[1]);
+ for (const auto &entity : nanoapps) {
+ std::cout << entity.first;
+ printNanoappHeader(entity.second);
+ }
+ break;
+ }
+ case load: {
+ loadNanoapp(cmdLine[1]);
+ break;
+ }
+ case unload: {
+ unloadNanoapp(cmdLine[1]);
+ break;
+ }
+ case query: {
+ queryNanoapps();
+ break;
+ }
+ default:
+ std::cout << kUsage;
+ }
+ } catch (std::system_error &e) {
+ std::cerr << e.what() << std::endl;
+ return -1;
}
-
- return success ? 0 : -1;
-}
+ return 0;
+}
\ No newline at end of file