• 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 <aidl/android/hardware/contexthub/BnContextHubCallback.h>
18 #include <aidl/android/hardware/contexthub/IContextHub.h>
19 #include <aidl/android/hardware/contexthub/NanoappBinary.h>
20 #include <android/binder_manager.h>
21 #include <android/binder_process.h>
22 #include <dirent.h>
23 #include <utils/String16.h>
24 
25 #include <cctype>
26 #include <filesystem>
27 #include <fstream>
28 #include <future>
29 #include <map>
30 #include <regex>
31 #include <stdexcept>
32 #include <string>
33 #include <vector>
34 
35 #include "chre_api/chre/version.h"
36 #include "chre_host/file_stream.h"
37 #include "chre_host/napp_header.h"
38 
39 using aidl::android::hardware::contexthub::AsyncEventType;
40 using aidl::android::hardware::contexthub::BnContextHubCallback;
41 using aidl::android::hardware::contexthub::ContextHubInfo;
42 using aidl::android::hardware::contexthub::ContextHubMessage;
43 using aidl::android::hardware::contexthub::HostEndpointInfo;
44 using aidl::android::hardware::contexthub::IContextHub;
45 using aidl::android::hardware::contexthub::NanoappBinary;
46 using aidl::android::hardware::contexthub::NanoappInfo;
47 using aidl::android::hardware::contexthub::NanSessionRequest;
48 using aidl::android::hardware::contexthub::Setting;
49 using android::chre::NanoAppBinaryHeader;
50 using android::chre::readFileContents;
51 using android::internal::ToString;
52 using ndk::ScopedAStatus;
53 
54 namespace {
55 // A default id 0 is used for every command requiring a context hub id. When
56 // this is not the case the id number should be one of the arguments of the
57 // commands.
58 constexpr uint32_t kContextHubId = 0;
59 constexpr int32_t kLoadTransactionId = 1;
60 constexpr int32_t kUnloadTransactionId = 2;
61 constexpr auto kTimeOutThresholdInSec = std::chrono::seconds(5);
62 // Locations should be searched in the sequence defined below:
63 const char *kPredefinedNanoappPaths[] = {
64     "/vendor/etc/chre/",
65     "/vendor/dsp/adsp/",
66     "/vendor/dsp/sdsp/",
67     "/vendor/lib/rfsa/adsp/",
68 };
69 // Please keep kUsage in alphabetical order
70 constexpr char kUsage[] = R"(
71 Usage: chre_aidl_hal_client COMMAND [ARGS]
72 COMMAND ARGS...:
73   connect                     - connect to HAL, register the callback and keep
74                                 the session alive while user can execute other
75                                 commands. Use `exit` to quit the session.
76   connectEndpoint <HEX_HOST_ENDPOINT_ID>
77                               - associate an endpoint with the current client
78                                 and notify HAL.
79   disableSetting <SETTING>    - disable a setting identified by a number defined
80                                 in android/hardware/contexthub/Setting.aidl.
81   disableTestMode             - disable test mode.
82   disconnectEndpoint <HEX_HOST_ENDPOINT_ID>
83                               - remove an endpoint with the current client and
84                                 notify HAL.
85   enableSetting <SETTING>     - enable a setting identified by a number defined
86                                 in android/hardware/contexthub/Setting.aidl.
87   enableTestMode              - enable test mode.
88   getContextHubs              - get all the context hubs.
89   list <PATH_OF_NANOAPPS>     - list all the nanoapps' header info in the path.
90   load <APP_NAME>             - load the nanoapp specified by the name.
91                                 If an absolute path like /path/to/awesome.so,
92                                 which is optional, is not provided then default
93                                 locations are searched.
94   query                       - show all loaded nanoapps (system apps excluded)
95   sendMessage <HEX_HOST_ENDPOINT_ID> <HEX_NANOAPP_ID | APP_NAME> <HEX_PAYLOAD>
96                               - send a payload to a nanoapp.
97   unload <HEX_NANOAPP_ID | APP_NAME>
98                               - unload the nanoapp specified by either the
99                                 nanoapp id in hex format or the app name.
100                                 If an absolute path like /path/to/awesome.so,
101                                 which is optional, is not provided then default
102                                 locations are searched.
103 )";
104 
throwError(const std::string & message)105 inline void throwError(const std::string &message) {
106   throw std::system_error{std::error_code(), message};
107 }
108 
isValidHexNumber(const std::string & number)109 bool isValidHexNumber(const std::string &number) {
110   if (number.empty() ||
111       (number.substr(0, 2) != "0x" && number.substr(0, 2) != "0X")) {
112     return false;
113   }
114   for (int i = 2; i < number.size(); i++) {
115     if (!isxdigit(number[i])) {
116       throwError("Hex app id " + number + " contains invalid character.");
117     }
118   }
119   return number.size() > 2;
120 }
121 
verifyAndConvertEndpointHexId(const std::string & number)122 uint16_t verifyAndConvertEndpointHexId(const std::string &number) {
123   // host endpoint id must be a 16-bits long hex number.
124   if (isValidHexNumber(number)) {
125     int convertedNumber = std::stoi(number, /* idx= */ nullptr, /* base= */ 16);
126     if (convertedNumber < std::numeric_limits<uint16_t>::max()) {
127       return static_cast<uint16_t>(convertedNumber);
128     }
129   }
130   throwError("host endpoint id must be a 16-bits long hex number.");
131   return 0;  // code never reached.
132 }
133 
isValidNanoappHexId(const std::string & number)134 bool isValidNanoappHexId(const std::string &number) {
135   if (!isValidHexNumber(number)) {
136     return false;
137   }
138   // Once the input has the hex prefix, an exception will be thrown if it is
139   // malformed because it shouldn't be treated as an app name anymore.
140   if (number.size() > 18) {
141     throwError("Hex app id must has a length of [3, 18] including the prefix.");
142   }
143   return true;
144 }
145 
parseAppVersion(uint32_t version)146 std::string parseAppVersion(uint32_t version) {
147   std::ostringstream stringStream;
148   stringStream << std::hex << "0x" << version << std::dec << " (v"
149                << CHRE_EXTRACT_MAJOR_VERSION(version) << "."
150                << CHRE_EXTRACT_MINOR_VERSION(version) << "."
151                << CHRE_EXTRACT_PATCH_VERSION(version) << ")";
152   return stringStream.str();
153 }
154 
parseTransactionId(int32_t transactionId)155 std::string parseTransactionId(int32_t transactionId) {
156   switch (transactionId) {
157     case kLoadTransactionId:
158       return "Loading";
159     case kUnloadTransactionId:
160       return "Unloading";
161     default:
162       return "Unknown";
163   }
164 }
165 
166 class ContextHubCallback : public BnContextHubCallback {
167  public:
handleNanoappInfo(const std::vector<NanoappInfo> & appInfo)168   ScopedAStatus handleNanoappInfo(
169       const std::vector<NanoappInfo> &appInfo) override {
170     std::cout << appInfo.size() << " nanoapps loaded" << std::endl;
171     for (const NanoappInfo &app : appInfo) {
172       std::cout << "appId: 0x" << std::hex << app.nanoappId << std::dec << " {"
173                 << "\n\tappVersion: " << parseAppVersion(app.nanoappVersion)
174                 << "\n\tenabled: " << (app.enabled ? "true" : "false")
175                 << "\n\tpermissions: " << ToString(app.permissions)
176                 << "\n\trpcServices: " << ToString(app.rpcServices) << "\n}"
177                 << std::endl;
178     }
179     setPromiseAndRefresh();
180     return ScopedAStatus::ok();
181   }
182 
handleContextHubMessage(const ContextHubMessage & message,const std::vector<std::string> &)183   ScopedAStatus handleContextHubMessage(
184       const ContextHubMessage &message,
185       const std::vector<std::string> & /*msgContentPerms*/) override {
186     std::cout << "Received a message with type " << message.messageType
187               << " size " << message.messageBody.size() << " from nanoapp 0x"
188               << std::hex << message.nanoappId
189               << " sent to the host endpoint 0x" << message.hostEndPoint
190               << std::endl;
191     std::cout << "message: 0x";
192     for (const uint8_t &data : message.messageBody) {
193       std::cout << std::hex << static_cast<uint32_t>(data);
194     }
195     std::cout << std::endl;
196     setPromiseAndRefresh();
197     return ScopedAStatus::ok();
198   }
199 
handleContextHubAsyncEvent(AsyncEventType)200   ScopedAStatus handleContextHubAsyncEvent(AsyncEventType /*event*/) override {
201     setPromiseAndRefresh();
202     return ScopedAStatus::ok();
203   }
204 
205   // Called after loading/unloading a nanoapp.
handleTransactionResult(int32_t transactionId,bool success)206   ScopedAStatus handleTransactionResult(int32_t transactionId,
207                                         bool success) override {
208     std::cout << parseTransactionId(transactionId) << " transaction is "
209               << (success ? "successful" : "failed") << std::endl;
210     setPromiseAndRefresh();
211     return ScopedAStatus::ok();
212   }
213 
handleNanSessionRequest(const NanSessionRequest &)214   ScopedAStatus handleNanSessionRequest(
215       const NanSessionRequest & /* request */) override {
216     return ScopedAStatus::ok();
217   }
218 
219   std::promise<void> promise;
220 
221  private:
setPromiseAndRefresh()222   void setPromiseAndRefresh() {
223     promise.set_value();
224     promise = std::promise<void>{};
225   }
226 };
227 
228 std::shared_ptr<IContextHub> gContextHub = nullptr;
229 std::shared_ptr<ContextHubCallback> gCallback = nullptr;
230 
231 /** Initializes gContextHub and register gCallback. */
getContextHub()232 std::shared_ptr<IContextHub> getContextHub() {
233   if (gContextHub == nullptr) {
234     auto aidlServiceName = std::string() + IContextHub::descriptor + "/default";
235     ndk::SpAIBinder binder(
236         AServiceManager_waitForService(aidlServiceName.c_str()));
237     if (binder.get() == nullptr) {
238       throwError("Could not find Context Hub HAL");
239     }
240     gContextHub = IContextHub::fromBinder(binder);
241     gCallback = ContextHubCallback::make<ContextHubCallback>();
242 
243     if (!gContextHub->registerCallback(kContextHubId, gCallback).isOk()) {
244       throwError("Failed to register the callback");
245     }
246   }
247   return gContextHub;
248 }
249 
printNanoappHeader(const NanoAppBinaryHeader & header)250 void printNanoappHeader(const NanoAppBinaryHeader &header) {
251   std::cout << " {"
252             << "\n\tappId: 0x" << std::hex << header.appId << std::dec
253             << "\n\tappVersion: " << parseAppVersion(header.appVersion)
254             << "\n\tflags: " << header.flags << "\n\ttarget CHRE API version: "
255             << static_cast<int>(header.targetChreApiMajorVersion) << "."
256             << static_cast<int>(header.targetChreApiMinorVersion) << "\n}"
257             << std::endl;
258 }
259 
findHeaderByName(const std::string & appName,const std::string & binaryPath)260 std::unique_ptr<NanoAppBinaryHeader> findHeaderByName(
261     const std::string &appName, const std::string &binaryPath) {
262   DIR *dir = opendir(binaryPath.c_str());
263   if (dir == nullptr) {
264     return nullptr;
265   }
266   std::regex regex(appName + ".napp_header");
267   std::cmatch match;
268 
269   std::unique_ptr<NanoAppBinaryHeader> result = nullptr;
270   for (struct dirent *entry; (entry = readdir(dir)) != nullptr;) {
271     if (!std::regex_match(entry->d_name, match, regex)) {
272       continue;
273     }
274     std::ifstream input(std::string(binaryPath) + "/" + entry->d_name,
275                         std::ios::binary);
276     result = std::make_unique<NanoAppBinaryHeader>();
277     input.read(reinterpret_cast<char *>(result.get()),
278                sizeof(NanoAppBinaryHeader));
279     break;
280   }
281   closedir(dir);
282   return result;
283 }
284 
readNanoappHeaders(std::map<std::string,NanoAppBinaryHeader> & nanoapps,const std::string & binaryPath)285 void readNanoappHeaders(std::map<std::string, NanoAppBinaryHeader> &nanoapps,
286                         const std::string &binaryPath) {
287   DIR *dir = opendir(binaryPath.c_str());
288   if (dir == nullptr) {
289     return;
290   }
291   std::regex regex("(\\w+)\\.napp_header");
292   std::cmatch match;
293   for (struct dirent *entry; (entry = readdir(dir)) != nullptr;) {
294     if (!std::regex_match(entry->d_name, match, regex)) {
295       continue;
296     }
297     std::ifstream input(std::string(binaryPath) + "/" + entry->d_name,
298                         std::ios::binary);
299     input.read(reinterpret_cast<char *>(&nanoapps[match[1]]),
300                sizeof(NanoAppBinaryHeader));
301   }
302   closedir(dir);
303 }
304 
verifyStatus(const std::string & operation,const ScopedAStatus & status)305 void verifyStatus(const std::string &operation, const ScopedAStatus &status) {
306   if (!status.isOk()) {
307     throwError(operation + " fails with abnormal status " +
308                ToString(status.getMessage()) + " error code " +
309                ToString(status.getServiceSpecificError()));
310   }
311 }
312 
verifyStatusAndSignal(const std::string & operation,const ScopedAStatus & status,const std::future<void> & future_signal)313 void verifyStatusAndSignal(const std::string &operation,
314                            const ScopedAStatus &status,
315                            const std::future<void> &future_signal) {
316   verifyStatus(operation, status);
317   std::future_status future_status =
318       future_signal.wait_for(kTimeOutThresholdInSec);
319   if (future_status != std::future_status::ready) {
320     throwError(operation + " doesn't finish within " +
321                ToString(kTimeOutThresholdInSec.count()) + " seconds");
322   }
323 }
324 
325 /** Finds the .napp_header file associated to the nanoapp.
326  *
327  * This function guarantees to return a non-null {@link NanoAppBinaryHeader}
328  * pointer. In case a .napp_header file cannot be found an exception will be
329  * raised.
330  *
331  * @param pathAndName name of the nanoapp that might be prefixed with it path.
332  * It will be normalized to the format of <absolute-path><name>.so at the end.
333  * For example, "abc" will be changed to "/path/to/abc.so".
334  * @return a unique pointer to the {@link NanoAppBinaryHeader} found
335  */
findHeaderAndNormalizePath(std::string & pathAndName)336 std::unique_ptr<NanoAppBinaryHeader> findHeaderAndNormalizePath(
337     std::string &pathAndName) {
338   // To match the file pattern of [path]<name>[.so]
339   std::regex pathNameRegex("(.*?)(\\w+)(\\.so)?");
340   std::smatch smatch;
341   if (!std::regex_match(pathAndName, smatch, pathNameRegex)) {
342     throwError("Invalid nanoapp: " + pathAndName);
343   }
344   std::string fullPath = smatch[1];
345   std::string appName = smatch[2];
346   // absolute path is provided:
347   if (!fullPath.empty() && fullPath[0] == '/') {
348     auto result = findHeaderByName(appName, fullPath);
349     if (result == nullptr) {
350       throwError("Unable to find the nanoapp header for " + pathAndName);
351     }
352     pathAndName = fullPath + appName + ".so";
353     return result;
354   }
355   // relative path is searched form predefined locations:
356   for (const std::string &predefinedPath : kPredefinedNanoappPaths) {
357     auto result = findHeaderByName(appName, predefinedPath);
358     if (result == nullptr) {
359       continue;
360     }
361     pathAndName = predefinedPath + appName + ".so";
362     std::cout << "Found the nanoapp header for " << pathAndName << std::endl;
363     return result;
364   }
365   throwError("Unable to find the nanoapp header for " + pathAndName);
366   return nullptr;
367 }
368 
getNanoappIdFrom(std::string & appIdOrName)369 int64_t getNanoappIdFrom(std::string &appIdOrName) {
370   int64_t appId;
371   if (isValidNanoappHexId(appIdOrName)) {
372     appId = std::stoll(appIdOrName, nullptr, 16);
373   } else {
374     // Treat the appIdOrName as the app name and try again
375     appId =
376         static_cast<int64_t>(findHeaderAndNormalizePath(appIdOrName)->appId);
377   }
378   return appId;
379 }
380 
getAllContextHubs()381 void getAllContextHubs() {
382   std::vector<ContextHubInfo> hubs{};
383   getContextHub()->getContextHubs(&hubs);
384   if (hubs.empty()) {
385     std::cerr << "Failed to get any context hub." << std::endl;
386     return;
387   }
388   for (const auto &hub : hubs) {
389     std::cout << "Context Hub " << hub.id << ": " << std::endl
390               << "  Name: " << hub.name << std::endl
391               << "  Vendor: " << hub.vendor << std::endl
392               << "  Max support message length (bytes): "
393               << hub.maxSupportedMessageLengthBytes << std::endl
394               << "  Version: " << static_cast<uint32_t>(hub.chreApiMajorVersion)
395               << "." << static_cast<uint32_t>(hub.chreApiMinorVersion)
396               << std::endl
397               << "  Chre platform id: 0x" << std::hex << hub.chrePlatformId
398               << std::endl;
399   }
400 }
401 
loadNanoapp(std::string & pathAndName)402 void loadNanoapp(std::string &pathAndName) {
403   auto header = findHeaderAndNormalizePath(pathAndName);
404   std::vector<uint8_t> soBuffer{};
405   if (!readFileContents(pathAndName.c_str(), soBuffer)) {
406     throwError("Failed to open the content of " + pathAndName);
407   }
408   NanoappBinary binary;
409   binary.nanoappId = static_cast<int64_t>(header->appId);
410   binary.customBinary = soBuffer;
411   binary.flags = static_cast<int32_t>(header->flags);
412   binary.targetChreApiMajorVersion =
413       static_cast<int8_t>(header->targetChreApiMajorVersion);
414   binary.targetChreApiMinorVersion =
415       static_cast<int8_t>(header->targetChreApiMinorVersion);
416   binary.nanoappVersion = static_cast<int32_t>(header->appVersion);
417 
418   auto status =
419       getContextHub()->loadNanoapp(kContextHubId, binary, kLoadTransactionId);
420   verifyStatusAndSignal(/* operation= */ "loading nanoapp " + pathAndName,
421                         status, gCallback->promise.get_future());
422 }
423 
unloadNanoapp(std::string & appIdOrName)424 void unloadNanoapp(std::string &appIdOrName) {
425   auto appId = getNanoappIdFrom(appIdOrName);
426   auto status = getContextHub()->unloadNanoapp(kContextHubId, appId,
427                                                kUnloadTransactionId);
428   verifyStatusAndSignal(/* operation= */ "unloading nanoapp " + appIdOrName,
429                         status, gCallback->promise.get_future());
430 }
431 
queryNanoapps()432 void queryNanoapps() {
433   auto status = getContextHub()->queryNanoapps(kContextHubId);
434   verifyStatusAndSignal(/* operation= */ "querying nanoapps", status,
435                         gCallback->promise.get_future());
436 }
437 
onEndpointConnected(const std::string & hexEndpointId)438 void onEndpointConnected(const std::string &hexEndpointId) {
439   auto contextHub = getContextHub();
440   uint16_t hostEndpointId = verifyAndConvertEndpointHexId(hexEndpointId);
441   HostEndpointInfo info = {
442       .hostEndpointId = hostEndpointId,
443       .type = HostEndpointInfo::Type::NATIVE,
444       .packageName = "chre_aidl_hal_client",
445       .attributionTag{},
446   };
447   // connect the endpoint to HAL
448   verifyStatus(/* operation= */ "connect endpoint",
449                contextHub->onHostEndpointConnected(info));
450   std::cout << "onHostEndpointConnected() is called. " << std::endl;
451 }
452 
onEndpointDisconnected(const std::string & hexEndpointId)453 void onEndpointDisconnected(const std::string &hexEndpointId) {
454   auto contextHub = getContextHub();
455   uint16_t hostEndpointId = verifyAndConvertEndpointHexId(hexEndpointId);
456   // disconnect the endpoint from HAL
457   verifyStatus(/* operation= */ "disconnect endpoint",
458                contextHub->onHostEndpointDisconnected(hostEndpointId));
459   std::cout << "onHostEndpointDisconnected() is called. " << std::endl;
460 }
461 
462 /** Sends a hexPayload from hexHostEndpointId to appIdOrName. */
sendMessageToNanoapp(const std::string & hexHostEndpointId,std::string & appIdOrName,const std::string & hexPayload)463 void sendMessageToNanoapp(const std::string &hexHostEndpointId,
464                           std::string &appIdOrName,
465                           const std::string &hexPayload) {
466   if (!isValidHexNumber(hexPayload)) {
467     throwError("Invalid hex payload.");
468   }
469   auto appId = getNanoappIdFrom(appIdOrName);
470   uint16_t hostEndpointId = verifyAndConvertEndpointHexId(hexHostEndpointId);
471   ContextHubMessage contextHubMessage = {
472       .nanoappId = appId,
473       .hostEndPoint = hostEndpointId,
474       .messageBody = {},
475       .permissions = {},
476   };
477   // populate the payload
478   for (int i = 2; i < hexPayload.size(); i += 2) {
479     contextHubMessage.messageBody.push_back(
480         std::stoi(hexPayload.substr(i, 2), /* idx= */ nullptr, /* base= */ 16));
481   }
482   // send the message
483   auto contextHub = getContextHub();
484   onEndpointConnected(hexHostEndpointId);
485   auto status = contextHub->sendMessageToHub(kContextHubId, contextHubMessage);
486   verifyStatusAndSignal(/* operation= */ "sending a message to " + appIdOrName,
487                         status, gCallback->promise.get_future());
488   onEndpointDisconnected(hexHostEndpointId);
489 }
490 
changeSetting(const std::string & setting,bool enabled)491 void changeSetting(const std::string &setting, bool enabled) {
492   auto contextHub = getContextHub();
493   int settingType = std::stoi(setting);
494   if (settingType < 1 || settingType > 7) {
495     throwError("setting type must be within [1, 7].");
496   }
497   ScopedAStatus status =
498       contextHub->onSettingChanged(static_cast<Setting>(settingType), enabled);
499   std::cout << "onSettingChanged is called to "
500             << (enabled ? "enable" : "disable") << " setting type "
501             << settingType << std::endl;
502   verifyStatus("change setting", status);
503 }
504 
enableTestModeOnContextHub()505 void enableTestModeOnContextHub() {
506   auto status = getContextHub()->setTestMode(true);
507   verifyStatusAndSignal(/* operation= */ "enabling test mode", status,
508                         gCallback->promise.get_future());
509 }
510 
disableTestModeOnContextHub()511 void disableTestModeOnContextHub() {
512   auto status = getContextHub()->setTestMode(false);
513   verifyStatusAndSignal(/* operation= */ "disabling test mode", status,
514                         gCallback->promise.get_future());
515 }
516 
517 // Please keep Command in alphabetical order
518 enum Command {
519   connect,
520   connectEndpoint,
521   disableSetting,
522   disableTestMode,
523   disconnectEndpoint,
524   enableSetting,
525   enableTestMode,
526   getContextHubs,
527   list,
528   load,
529   query,
530   sendMessage,
531   unload,
532   unsupported
533 };
534 
535 struct CommandInfo {
536   Command cmd;
537   u_int8_t numofArgs;  // including cmd;
538 };
539 
parseCommand(const std::vector<std::string> & cmdLine)540 Command parseCommand(const std::vector<std::string> &cmdLine) {
541   std::map<std::string, CommandInfo> commandMap{
542       {"connect", {connect, 1}},
543       {"connectEndpoint", {connectEndpoint, 2}},
544       {"disableSetting", {disableSetting, 2}},
545       {"disableTestMode", {disableTestMode, 1}},
546       {"disconnectEndpoint", {disconnectEndpoint, 2}},
547       {"enableSetting", {enableSetting, 2}},
548       {"enableTestMode", {enableTestMode, 1}},
549       {"getContextHubs", {getContextHubs, 1}},
550       {"list", {list, 2}},
551       {"load", {load, 2}},
552       {"query", {query, 1}},
553       {"sendMessage", {sendMessage, 4}},
554       {"unload", {unload, 2}},
555   };
556   if (cmdLine.empty() || commandMap.find(cmdLine[0]) == commandMap.end()) {
557     return unsupported;
558   }
559   auto cmdInfo = commandMap.at(cmdLine[0]);
560   return cmdLine.size() == cmdInfo.numofArgs ? cmdInfo.cmd : unsupported;
561 }
562 
executeCommand(std::vector<std::string> cmdLine)563 void executeCommand(std::vector<std::string> cmdLine) {
564   switch (parseCommand(cmdLine)) {
565     case connectEndpoint: {
566       onEndpointConnected(cmdLine[1]);
567       break;
568     }
569     case disableSetting: {
570       changeSetting(cmdLine[1], false);
571       break;
572     }
573     case disableTestMode: {
574       disableTestModeOnContextHub();
575       break;
576     }
577     case disconnectEndpoint: {
578       onEndpointDisconnected(cmdLine[1]);
579       break;
580     }
581     case enableSetting: {
582       changeSetting(cmdLine[1], true);
583       break;
584     }
585     case enableTestMode: {
586       enableTestModeOnContextHub();
587       break;
588     }
589     case getContextHubs: {
590       getAllContextHubs();
591       break;
592     }
593     case list: {
594       std::map<std::string, NanoAppBinaryHeader> nanoapps{};
595       readNanoappHeaders(nanoapps, cmdLine[1]);
596       for (const auto &entity : nanoapps) {
597         std::cout << entity.first;
598         printNanoappHeader(entity.second);
599       }
600       break;
601     }
602     case load: {
603       loadNanoapp(cmdLine[1]);
604       break;
605     }
606     case query: {
607       queryNanoapps();
608       break;
609     }
610     case sendMessage: {
611       sendMessageToNanoapp(cmdLine[1], cmdLine[2], cmdLine[3]);
612       break;
613     }
614     case unload: {
615       unloadNanoapp(cmdLine[1]);
616       break;
617     }
618     default:
619       std::cout << kUsage;
620   }
621 }
622 
getCommandLine()623 std::vector<std::string> getCommandLine() {
624   std::string input;
625   std::cout << "> ";
626   std::getline(std::cin, input);
627   input.push_back('\n');
628   std::vector<std::string> result{};
629   for (int begin = 0, end = 0; end < input.size();) {
630     if (isspace(input[begin])) {
631       end = begin = begin + 1;
632       continue;
633     }
634     if (!isspace(input[end])) {
635       end += 1;
636       continue;
637     }
638     result.push_back(input.substr(begin, end - begin));
639     begin = end;
640   }
641   return result;
642 }
643 
connectToHal()644 void connectToHal() {
645   auto hub = getContextHub();
646   std::cout << "Connected to context hub." << std::endl;
647   while (true) {
648     auto cmdLine = getCommandLine();
649     if (cmdLine.empty()) {
650       continue;
651     }
652     if (cmdLine.size() == 1 && cmdLine[0] == "connect") {
653       std::cout << "Already in a live session." << std::endl;
654       continue;
655     }
656     if (cmdLine.size() == 1 && cmdLine[0] == "exit") {
657       break;
658     }
659     try {
660       executeCommand(cmdLine);
661     } catch (std::system_error &e) {
662       std::cerr << e.what() << std::endl;
663     }
664   }
665 }
666 }  // anonymous namespace
667 
main(int argc,char * argv[])668 int main(int argc, char *argv[]) {
669   // Start binder thread pool to enable callbacks.
670   ABinderProcess_startThreadPool();
671 
672   std::vector<std::string> cmdLine{};
673   for (int i = 1; i < argc; i++) {
674     cmdLine.emplace_back(argv[i]);
675   }
676   if (cmdLine.size() == 1 && cmdLine[0] == "connect") {
677     connectToHal();
678     return 0;
679   }
680   try {
681     executeCommand(cmdLine);
682   } catch (std::system_error &e) {
683     std::cerr << e.what() << std::endl;
684     return -1;
685   }
686   return 0;
687 }