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 }