1 /*
2 * Copyright (C) 2019 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 <cutils/sockets.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <utils/StrongPointer.h>
22
23 #include <chrono>
24 #include <cinttypes>
25 #include <condition_variable>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <fstream>
29 #include <mutex>
30 #include <string>
31 #include <thread>
32 #include <unordered_map>
33 #include <vector>
34
35 #include "chre/util/nanoapp/app_id.h"
36 #include "chre/util/system/napp_header_utils.h"
37 #include "chre/version.h"
38 #include "chre_host/host_protocol_host.h"
39 #include "chre_host/log.h"
40 #include "chre_host/socket_client.h"
41 #include "generated/chre_power_test_generated.h"
42
43 /**
44 * @file
45 * A test utility that connects to the CHRE daemon and provides commands to take
46 * control the power test nanoapp located at system/chre/apps/power_test
47 *
48 * Usage:
49 * chre_power_test_client load <optional: tcm> <optional: path>
50 * chre_power_test_client unload <optional: tcm>
51 * chre_power_test_client unloadall
52 * chre_power_test_client timer <optional: tcm> <enable> <interval_ns>
53 * chre_power_test_client wifi <optional: tcm> <enable> <interval_ns>
54 * <optional: wifi_scan_type>
55 * <optional: wifi_radio_chain>
56 * <optional: wifi_channel_set>
57 * chre_power_test_client gnss <optional: tcm> <enable> <interval_ms>
58 * <optional: next_fix_ms>
59 * chre_power_test_client cell <optional: tcm> <enable> <interval_ns>
60 * chre_power_test_client audio <optional: tcm> <enable> <duration_ns>
61 * chre_power_test_client sensor <optional: tcm> <enable> <sensor_type>
62 * <interval_ns> <optional: latency_ns>
63 * chre_power_test_client breakit <optional: tcm> <enable>
64 * chre_power_test_client gnss_meas <optional: tcm> <enable> <interval_ms>
65 *
66 * Command:
67 * load: load power test nanoapp to CHRE
68 * unload: unload power test nanoapp from CHRE
69 * unloadall: unload all nanoapps in CHRE
70 * timer: start/stop timer wake up
71 * wifi: start/stop periodic wifi scan
72 * gnss: start/stop periodic GPS scan
73 * cell: start/stop periodic cellular scan
74 * audio: start/stop periodic audio capture
75 * sensor: start/stop periodic sensor sampling
76 * breakit: start/stop all action for stress tests
77 * gnss_meas: start/stop periodic GNSS measurement
78 *
79 * <optional: tcm>: tcm for micro image, default for big image
80 * <enable>: enable/disable
81 *
82 * <sensor_type>:
83 * accelerometer
84 * instant_motion
85 * stationary
86 * gyroscope
87 * uncalibrated_gyroscope
88 * geomagnetic
89 * uncalibrated_geomagnetic
90 * pressure
91 * light
92 * proximity
93 * step
94 * step_counter
95 * uncalibrated_accelerometer
96 * accelerometer_temperature
97 * gyroscope_temperature
98 * geomagnetic_temperature
99 *
100 * For instant_motion and stationary sensor, it is not necessary to provide the
101 * interval and latency
102 *
103 * <wifi_scan_type>:
104 * active
105 * active_passive_dfs
106 * passive
107 * no_preference (default when omitted)
108 *
109 * <wifi_radio_chain>:
110 * default (default when omitted)
111 * low_latency
112 * low_power
113 * high_accuracy
114 *
115 * <wifi_channel_set>:
116 * non_dfs (default when omitted)
117 * all
118 */
119
120 using android::sp;
121 using android::chre::FragmentedLoadTransaction;
122 using android::chre::getStringFromByteVector;
123 using android::chre::HostProtocolHost;
124 using android::chre::IChreMessageHandlers;
125 using android::chre::SocketClient;
126 using chre::power_test::MessageType;
127 using chre::power_test::SensorType;
128 using chre::power_test::WifiChannelSet;
129 using chre::power_test::WifiRadioChain;
130 using chre::power_test::WifiScanType;
131 using flatbuffers::FlatBufferBuilder;
132 using std::string;
133
134 // Aliased for consistency with the way these symbols are referenced in
135 // CHRE-side code
136 namespace fbs = ::chre::fbs;
137 namespace ptest = ::chre::power_test;
138
139 namespace {
140
141 constexpr uint16_t kHostEndpoint = 0xfffd;
142
143 constexpr uint32_t kAppVersion = 0x00020000;
144 constexpr uint32_t kApiVersion = CHRE_API_VERSION;
145 constexpr uint64_t kPowerTestAppId = 0x012345678900000f;
146 constexpr uint64_t kPowerTestTcmAppId = 0x0123456789000010;
147 constexpr uint64_t kUint64Max = std::numeric_limits<uint64_t>::max();
148
149 constexpr auto kTimeout = std::chrono::seconds(10);
150
151 const string kPowerTestName = "power_test.so";
152 const string kPowerTestTcmName = "power_test_tcm.so";
153 std::condition_variable kReadyCond;
154 std::mutex kReadyMutex;
155 std::unique_lock<std::mutex> kReadyCondLock(kReadyMutex);
156
157 enum class Command : uint32_t {
158 kUnloadAll = 0,
159 kLoad,
160 kUnload,
161 kTimer,
162 kWifi,
163 kGnss,
164 kCell,
165 kAudio,
166 kSensor,
167 kBreakIt,
168 kGnssMeas
169 };
170
171 std::unordered_map<string, Command> commandMap{
172 {"unloadall", Command::kUnloadAll}, {"load", Command::kLoad},
173 {"unload", Command::kUnload}, {"timer", Command::kTimer},
174 {"wifi", Command::kWifi}, {"gnss", Command::kGnss},
175 {"cell", Command::kCell}, {"audio", Command::kAudio},
176 {"sensor", Command::kSensor}, {"breakit", Command::kBreakIt},
177 {"gnss_meas", Command::kGnssMeas}};
178
179 std::unordered_map<string, MessageType> messageTypeMap{
180 {"timer", MessageType::TIMER_TEST},
181 {"wifi", MessageType::WIFI_SCAN_TEST},
182 {"gnss", MessageType::GNSS_LOCATION_TEST},
183 {"cell", MessageType::CELL_QUERY_TEST},
184 {"audio", MessageType::AUDIO_REQUEST_TEST},
185 {"sensor", MessageType::SENSOR_REQUEST_TEST},
186 {"breakit", MessageType::BREAK_IT_TEST},
187 {"gnss_meas", MessageType::GNSS_MEASUREMENT_TEST}};
188
189 std::unordered_map<string, SensorType> sensorTypeMap{
190 {"accelerometer", SensorType::ACCELEROMETER},
191 {"instant_motion", SensorType::INSTANT_MOTION_DETECT},
192 {"stationary", SensorType::STATIONARY_DETECT},
193 {"gyroscope", SensorType::GYROSCOPE},
194 {"uncalibrated_gyroscope", SensorType::UNCALIBRATED_GYROSCOPE},
195 {"geomagnetic", SensorType::GEOMAGNETIC_FIELD},
196 {"uncalibrated_geomagnetic", SensorType::UNCALIBRATED_GEOMAGNETIC_FIELD},
197 {"pressure", SensorType::PRESSURE},
198 {"light", SensorType::LIGHT},
199 {"proximity", SensorType::PROXIMITY},
200 {"step", SensorType::STEP_DETECT},
201 {"step_counter", SensorType::STEP_COUNTER},
202 {"uncalibrated_accelerometer", SensorType::UNCALIBRATED_ACCELEROMETER},
203 {"accelerometer_temperature", SensorType::ACCELEROMETER_TEMPERATURE},
204 {"gyroscope_temperature", SensorType::GYROSCOPE_TEMPERATURE},
205 {"geomagnetic_temperature", SensorType::GEOMAGNETIC_FIELD_TEMPERATURE}};
206
207 std::unordered_map<string, WifiScanType> wifiScanTypeMap{
208 {"active", WifiScanType::ACTIVE},
209 {"active_passive_dfs", WifiScanType::ACTIVE_PLUS_PASSIVE_DFS},
210 {"passive", WifiScanType::PASSIVE},
211 {"no_preference", WifiScanType::NO_PREFERENCE}};
212
213 std::unordered_map<string, WifiRadioChain> wifiRadioChainMap{
214 {"default", WifiRadioChain::DEFAULT},
215 {"low_latency", WifiRadioChain::LOW_LATENCY},
216 {"low_power", WifiRadioChain::LOW_POWER},
217 {"high_accuracy", WifiRadioChain::HIGH_ACCURACY}};
218
219 std::unordered_map<string, WifiChannelSet> wifiChannelSetMap{
220 {"non_dfs", WifiChannelSet::NON_DFS}, {"all", WifiChannelSet::ALL}};
221
wifiScanTypeMatch(const string & name,WifiScanType * scanType)222 bool wifiScanTypeMatch(const string &name, WifiScanType *scanType) {
223 if (wifiScanTypeMap.find(name) != wifiScanTypeMap.end()) {
224 *scanType = wifiScanTypeMap[name];
225 return true;
226 }
227 return false;
228 }
229
wifiRadioChainMatch(const string & name,WifiRadioChain * radioChain)230 bool wifiRadioChainMatch(const string &name, WifiRadioChain *radioChain) {
231 if (wifiRadioChainMap.find(name) != wifiRadioChainMap.end()) {
232 *radioChain = wifiRadioChainMap[name];
233 return true;
234 }
235 return false;
236 }
237
wifiChannelSetMatch(const string & name,WifiChannelSet * channelSet)238 bool wifiChannelSetMatch(const string &name, WifiChannelSet *channelSet) {
239 if (wifiChannelSetMap.find(name) != wifiChannelSetMap.end()) {
240 *channelSet = wifiChannelSetMap[name];
241 return true;
242 }
243 return false;
244 }
245
246 class SocketCallbacks : public SocketClient::ICallbacks,
247 public IChreMessageHandlers {
248 public:
SocketCallbacks(std::condition_variable & readyCond)249 SocketCallbacks(std::condition_variable &readyCond)
250 : mConditionVariable(readyCond) {}
251
onMessageReceived(const void * data,size_t length)252 void onMessageReceived(const void *data, size_t length) override {
253 if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
254 LOGE("Failed to decode message");
255 }
256 }
257
onConnected()258 void onConnected() override {
259 LOGI("Socket (re)connected");
260 }
261
onConnectionAborted()262 void onConnectionAborted() override {
263 LOGI("Socket (re)connection aborted");
264 }
265
onDisconnected()266 void onDisconnected() override {
267 LOGI("Socket disconnected");
268 }
269
handleNanoappMessage(const fbs::NanoappMessageT & message)270 void handleNanoappMessage(const fbs::NanoappMessageT &message) override {
271 LOGI("Got message from nanoapp 0x%" PRIx64 " to endpoint 0x%" PRIx16
272 " with type 0x%" PRIx32 " and length %zu",
273 message.app_id, message.host_endpoint, message.message_type,
274 message.message.size());
275 if (message.message_type ==
276 static_cast<uint32_t>(MessageType::NANOAPP_RESPONSE)) {
277 handlePowerTestNanoappResponse(message.message);
278 }
279 }
280
handlePowerTestNanoappResponse(const std::vector<uint8_t> & message)281 void handlePowerTestNanoappResponse(const std::vector<uint8_t> &message) {
282 auto response =
283 flatbuffers::GetRoot<ptest::NanoappResponseMessage>(message.data());
284 flatbuffers::Verifier verifier(message.data(), message.size());
285 bool success = response->Verify(verifier);
286 mSuccess = success ? response->success() : false;
287 mConditionVariable.notify_all();
288 }
289
handleNanoappListResponse(const fbs::NanoappListResponseT & response)290 void handleNanoappListResponse(
291 const fbs::NanoappListResponseT &response) override {
292 LOGI("Got nanoapp list response with %zu apps:", response.nanoapps.size());
293 mAppIdVector.clear();
294 for (const auto &nanoapp : response.nanoapps) {
295 LOGI("App ID 0x%016" PRIx64 " version 0x%" PRIx32
296 " permissions 0x%" PRIx32 " enabled %d system %d",
297 nanoapp->app_id, nanoapp->version, nanoapp->permissions,
298 nanoapp->enabled, nanoapp->is_system);
299 mAppIdVector.push_back(nanoapp->app_id);
300 }
301 mConditionVariable.notify_all();
302 }
303
handleLoadNanoappResponse(const fbs::LoadNanoappResponseT & response)304 void handleLoadNanoappResponse(
305 const fbs::LoadNanoappResponseT &response) override {
306 LOGI("Got load nanoapp response, transaction ID 0x%" PRIx32 " result %d",
307 response.transaction_id, response.success);
308 mSuccess = response.success;
309 mConditionVariable.notify_all();
310 }
311
handleUnloadNanoappResponse(const fbs::UnloadNanoappResponseT & response)312 void handleUnloadNanoappResponse(
313 const fbs::UnloadNanoappResponseT &response) override {
314 LOGI("Got unload nanoapp response, transaction ID 0x%" PRIx32 " result %d",
315 response.transaction_id, response.success);
316 mSuccess = response.success;
317 mConditionVariable.notify_all();
318 }
319
actionSucceeded()320 bool actionSucceeded() {
321 return mSuccess;
322 }
323
getAppIdVector()324 std::vector<uint64_t> &getAppIdVector() {
325 return mAppIdVector;
326 }
327
328 private:
329 bool mSuccess = false;
330 std::condition_variable &mConditionVariable;
331 std::vector<uint64_t> mAppIdVector;
332 };
333
requestNanoappList(SocketClient & client)334 bool requestNanoappList(SocketClient &client) {
335 FlatBufferBuilder builder(64);
336 HostProtocolHost::encodeNanoappListRequest(builder);
337
338 LOGI("Sending app list request (%" PRIu32 " bytes)", builder.GetSize());
339 if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
340 LOGE("Failed to send message");
341 return false;
342 }
343 return true;
344 }
345
sendLoadNanoappRequest(SocketClient & client,const char * filename,uint64_t appId,uint32_t appVersion,uint32_t apiVersion,bool tcmApp)346 bool sendLoadNanoappRequest(SocketClient &client, const char *filename,
347 uint64_t appId, uint32_t appVersion,
348 uint32_t apiVersion, bool tcmApp) {
349 std::ifstream file(filename, std::ios::binary | std::ios::ate);
350 if (!file) {
351 LOGE("Couldn't open file '%s': %s", filename, strerror(errno));
352 return false;
353 }
354 ssize_t size = file.tellg();
355 file.seekg(0, std::ios::beg);
356
357 std::vector<uint8_t> buffer(size);
358 if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
359 LOGE("Couldn't read from file: %s", strerror(errno));
360 file.close();
361 return false;
362 }
363
364 // All loaded nanoapps must be signed currently.
365 uint32_t appFlags = CHRE_NAPP_HEADER_SIGNED;
366 if (tcmApp) {
367 appFlags |= CHRE_NAPP_HEADER_TCM_CAPABLE;
368 }
369
370 // Perform loading with 1 fragment for simplicity
371 FlatBufferBuilder builder(size + 128);
372 FragmentedLoadTransaction transaction = FragmentedLoadTransaction(
373 1 /* transactionId */, appId, appVersion, appFlags, apiVersion, buffer,
374 buffer.size() /* fragmentSize */);
375 HostProtocolHost::encodeFragmentedLoadNanoappRequest(
376 builder, transaction.getNextRequest());
377 LOGI("Sending load nanoapp request (%" PRIu32
378 " bytes total w/ %zu bytes of payload)",
379 builder.GetSize(), buffer.size());
380 bool success =
381 client.sendMessage(builder.GetBufferPointer(), builder.GetSize());
382 if (!success) {
383 LOGE("Failed to send message");
384 }
385 file.close();
386 return success;
387 }
388
loadNanoapp(SocketClient & client,sp<SocketCallbacks> callbacks,const char * filename,uint64_t appId,uint32_t appVersion,uint32_t apiVersion,bool tcmApp)389 bool loadNanoapp(SocketClient &client, sp<SocketCallbacks> callbacks,
390 const char *filename, uint64_t appId, uint32_t appVersion,
391 uint32_t apiVersion, bool tcmApp) {
392 if (!sendLoadNanoappRequest(client, filename, appId, appVersion, apiVersion,
393 tcmApp)) {
394 return false;
395 }
396 auto status = kReadyCond.wait_for(kReadyCondLock, kTimeout);
397 bool success =
398 (status == std::cv_status::no_timeout && callbacks->actionSucceeded());
399 LOGI("Loaded the nanoapp with appId: %" PRIx64 " success: %d", appId,
400 success);
401 return success;
402 }
403
sendUnloadNanoappRequest(SocketClient & client,uint64_t appId)404 bool sendUnloadNanoappRequest(SocketClient &client, uint64_t appId) {
405 FlatBufferBuilder builder(64);
406 constexpr uint32_t kTransactionId = 4321;
407 HostProtocolHost::encodeUnloadNanoappRequest(
408 builder, kTransactionId, appId, true /* allowSystemNanoappUnload */);
409
410 LOGI("Sending unload request for nanoapp 0x%016" PRIx64 " (size %" PRIu32 ")",
411 appId, builder.GetSize());
412 if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
413 LOGE("Failed to send message");
414 return false;
415 }
416 return true;
417 }
418
unloadNanoapp(SocketClient & client,sp<SocketCallbacks> callbacks,uint64_t appId)419 bool unloadNanoapp(SocketClient &client, sp<SocketCallbacks> callbacks,
420 uint64_t appId) {
421 if (!sendUnloadNanoappRequest(client, appId)) {
422 return false;
423 }
424 auto status = kReadyCond.wait_for(kReadyCondLock, kTimeout);
425 bool success =
426 (status == std::cv_status::no_timeout && callbacks->actionSucceeded());
427 LOGI("Unloaded the nanoapp with appId: %" PRIx64 " success: %d", appId,
428 success);
429 return success;
430 }
431
listNanoapps(SocketClient & client)432 bool listNanoapps(SocketClient &client) {
433 if (!requestNanoappList(client)) {
434 LOGE("Failed in listing nanoapps");
435 return false;
436 }
437 auto status = kReadyCond.wait_for(kReadyCondLock, kTimeout);
438 bool success = (status == std::cv_status::no_timeout);
439 LOGI("Listed nanoapps success: %d", success);
440 return success;
441 }
442
unloadAllNanoapps(SocketClient & client,sp<SocketCallbacks> callbacks)443 bool unloadAllNanoapps(SocketClient &client, sp<SocketCallbacks> callbacks) {
444 if (!listNanoapps(client)) {
445 return false;
446 }
447 for (auto appId : callbacks->getAppIdVector()) {
448 if (!unloadNanoapp(client, callbacks, appId)) {
449 LOGE("Failed in unloading nanoapps, unloading aborted");
450 return false;
451 }
452 }
453 LOGI("Unloaded all nanoapps succeeded");
454 return true;
455 }
456
isTcmArgSpecified(std::vector<string> & args)457 bool isTcmArgSpecified(std::vector<string> &args) {
458 return !args.empty() && args[0] == "tcm";
459 }
460
getId(std::vector<string> & args)461 inline uint64_t getId(std::vector<string> &args) {
462 return isTcmArgSpecified(args) ? kPowerTestTcmAppId : kPowerTestAppId;
463 }
464
searchPath(const string & name)465 const string searchPath(const string &name) {
466 const string kAdspPath = "vendor/dsp/adsp/" + name;
467 const string kSdspPath = "vendor/dsp/sdsp/" + name;
468 const string kEtcPath = "vendor/etc/chre/" + name;
469
470 struct stat buf;
471 if (stat(kAdspPath.c_str(), &buf) == 0) {
472 return kAdspPath;
473 } else if (stat(kSdspPath.c_str(), &buf) == 0) {
474 return kSdspPath;
475 } else {
476 return kEtcPath;
477 }
478 }
479
480 /**
481 * When user provides the customized path in tcm mode, the args[1] is the path.
482 * In this case, the args[0] has to be "tcm". When user provide customized path
483 * for non-tcm mode, the args[0] is the path.
484 */
485
getPath(std::vector<string> & args)486 inline const string getPath(std::vector<string> &args) {
487 if (args.empty()) {
488 return searchPath(kPowerTestName);
489 }
490 if (args[0] == "tcm") {
491 if (args.size() > 1) {
492 return args[1];
493 }
494 return searchPath(kPowerTestTcmName);
495 }
496 return args[0];
497 }
498
getNanoseconds(std::vector<string> & args,size_t index)499 inline uint64_t getNanoseconds(std::vector<string> &args, size_t index) {
500 return args.size() > index ? strtoull(args[index].c_str(), NULL, 0) : 0;
501 }
502
getMilliseconds(std::vector<string> & args,size_t index)503 inline uint32_t getMilliseconds(std::vector<string> &args, size_t index) {
504 return args.size() > index ? strtoul(args[index].c_str(), NULL, 0) : 0;
505 }
506
isLoaded(SocketClient & client,sp<SocketCallbacks> callbacks,std::vector<string> & args)507 bool isLoaded(SocketClient &client, sp<SocketCallbacks> callbacks,
508 std::vector<string> &args) {
509 uint64_t id = getId(args);
510 if (!listNanoapps(client)) {
511 return false;
512 }
513 for (auto appId : callbacks->getAppIdVector()) {
514 if (appId == id) {
515 LOGI("The required nanoapp was loaded");
516 return true;
517 }
518 }
519 LOGE("The required nanoapp was not loaded");
520 return false;
521 }
522
validateSensorArguments(std::vector<string> & args)523 bool validateSensorArguments(std::vector<string> &args) {
524 if (args.size() < 3) {
525 LOGE("Sensor type is required");
526 return false;
527 }
528
529 if (sensorTypeMap.find(args[2]) == sensorTypeMap.end()) {
530 LOGE("Invalid sensor type");
531 return false;
532 }
533
534 SensorType sensorType = sensorTypeMap[args[2]];
535 if (sensorType == SensorType::STATIONARY_DETECT ||
536 sensorType == SensorType::INSTANT_MOTION_DETECT)
537 return true;
538
539 uint64_t intervalNanoseconds = getNanoseconds(args, 3);
540 uint64_t latencyNanoseconds = getNanoseconds(args, 4);
541 if (intervalNanoseconds == 0) {
542 LOGE("Non zero sensor sampling interval is required when enable");
543 return false;
544 }
545 if (latencyNanoseconds != 0 && latencyNanoseconds < intervalNanoseconds) {
546 LOGE("The latency is not zero and smaller than the interval");
547 return false;
548 }
549 return true;
550 }
551
validateWifiArguments(std::vector<string> & args)552 bool validateWifiArguments(std::vector<string> &args) {
553 if (args.size() < 3) {
554 LOGE("The interval is required");
555 return false;
556 }
557
558 bool valid = true;
559 WifiScanType scanType;
560 WifiRadioChain radioChain;
561 WifiChannelSet channelSet;
562 for (int i = 3; i < 6 && args.size() > i && valid; i++) {
563 valid = wifiScanTypeMatch(args[i], &scanType) ||
564 wifiRadioChainMatch(args[i], &radioChain) ||
565 wifiChannelSetMatch(args[i], &channelSet);
566 if (!valid) {
567 LOGE("Invalid WiFi scan parameters: %s", args[i].c_str());
568 return false;
569 }
570 }
571
572 uint64_t intervalNanoseconds = getNanoseconds(args, 2);
573 if (intervalNanoseconds == 0) {
574 LOGE("Non-zero WiFi request interval is required");
575 return false;
576 }
577
578 return true;
579 }
580
validateArguments(Command commandEnum,std::vector<string> & args)581 bool validateArguments(Command commandEnum, std::vector<string> &args) {
582 // Commands: unloadall, load, unload
583 if (static_cast<uint32_t>(commandEnum) < 3) return true;
584
585 // The other commands.
586 if (args.empty()) {
587 LOGE("Not enough parameters");
588 return false;
589 }
590
591 // For non tcm option, add one item to the head of args to align argument
592 // position with that with tcm option.
593 if (args[0] != "tcm") args.insert(args.begin(), "");
594 if (args.size() < 2) {
595 LOGE("Not enough parameters");
596 return false;
597 }
598
599 if (args[1] != "enable" && args[1] != "disable") {
600 LOGE("<enable> was neither enable nor disable");
601 return false;
602 }
603
604 if (commandEnum == Command::kBreakIt) return true;
605
606 if (args[1] == "disable") {
607 if (commandEnum != Command::kSensor) return true;
608 if (args.size() > 2 && sensorTypeMap.find(args[2]) != sensorTypeMap.end())
609 return true;
610 LOGE("No sensor type or invalid sensor type");
611 return false;
612 }
613
614 // Case of "enable":
615 if (commandEnum == Command::kSensor) {
616 return validateSensorArguments(args);
617 } else if (commandEnum == Command::kWifi) {
618 return validateWifiArguments(args);
619 } else {
620 if (args.size() < 3) {
621 LOGE("The interval or duration was not provided");
622 return false;
623 }
624
625 // For checking if the interval is 0. The getNanoseconds and
626 // and the getMilliseconds are exchangable in this case.
627 if (getNanoseconds(args, 2) == 0) {
628 LOGE("Non zero interval or duration is required when enable");
629 return false;
630 }
631 return true;
632 }
633 }
634
createTimerMessage(FlatBufferBuilder & fbb,std::vector<string> & args)635 void createTimerMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
636 bool enable = (args[1] == "enable");
637 uint64_t intervalNanoseconds = getNanoseconds(args, 2);
638 fbb.Finish(ptest::CreateTimerMessage(fbb, enable, intervalNanoseconds));
639 LOGI("Created TimerMessage, enable %d, wakeup interval ns %" PRIu64, enable,
640 intervalNanoseconds);
641 }
642
createWifiMessage(FlatBufferBuilder & fbb,std::vector<string> & args)643 void createWifiMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
644 bool enable = (args[1] == "enable");
645 uint64_t intervalNanoseconds = getNanoseconds(args, 2);
646 WifiScanType scanType = WifiScanType::NO_PREFERENCE;
647 WifiRadioChain radioChain = WifiRadioChain::DEFAULT;
648 WifiChannelSet channelSet = WifiChannelSet::NON_DFS;
649
650 // Check for the 3 optional parameters.
651 bool valid = true;
652 for (int i = 3; i < 6 && args.size() > i && valid; i++) {
653 valid = wifiScanTypeMatch(args[i], &scanType) ||
654 wifiRadioChainMatch(args[i], &radioChain) ||
655 wifiChannelSetMatch(args[i], &channelSet);
656 }
657
658 fbb.Finish(ptest::CreateWifiScanMessage(fbb, enable, intervalNanoseconds,
659 scanType, radioChain, channelSet));
660 LOGI("Created WifiScanMessage, enable %d, scan interval ns %" PRIu64
661 " scan type %" PRIu8 " radio chain %" PRIu8 " channel set %" PRIu8,
662 enable, intervalNanoseconds, static_cast<uint8_t>(scanType),
663 static_cast<uint8_t>(radioChain), static_cast<uint8_t>(channelSet));
664 }
665
createGnssMessage(FlatBufferBuilder & fbb,std::vector<string> & args)666 void createGnssMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
667 bool enable = (args[1] == "enable");
668 uint32_t intervalMilliseconds = getMilliseconds(args, 2);
669 uint32_t toNextFixMilliseconds = getMilliseconds(args, 3);
670 fbb.Finish(ptest::CreateGnssLocationMessage(fbb, enable, intervalMilliseconds,
671 toNextFixMilliseconds));
672 LOGI("Created GnssLocationMessage, enable %d, scan interval ms %" PRIu32
673 " min time to next fix ms %" PRIu32,
674 enable, intervalMilliseconds, toNextFixMilliseconds);
675 }
676
createCellMessage(FlatBufferBuilder & fbb,std::vector<string> & args)677 void createCellMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
678 bool enable = (args[1] == "enable");
679 uint64_t intervalNanoseconds = getNanoseconds(args, 2);
680 fbb.Finish(ptest::CreateCellQueryMessage(fbb, enable, intervalNanoseconds));
681 LOGI("Created CellQueryMessage, enable %d, query interval ns %" PRIu64,
682 enable, intervalNanoseconds);
683 }
684
createAudioMessage(FlatBufferBuilder & fbb,std::vector<string> & args)685 void createAudioMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
686 bool enable = (args[1] == "enable");
687 uint64_t durationNanoseconds = getNanoseconds(args, 2);
688 fbb.Finish(
689 ptest::CreateAudioRequestMessage(fbb, enable, durationNanoseconds));
690 LOGI("Created AudioRequestMessage, enable %d, buffer duration ns %" PRIu64,
691 enable, durationNanoseconds);
692 }
693
createSensorMessage(FlatBufferBuilder & fbb,std::vector<string> & args)694 void createSensorMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
695 bool enable = (args[1] == "enable");
696 SensorType sensorType = sensorTypeMap[args[2]];
697 uint64_t intervalNanoseconds = getNanoseconds(args, 3);
698 uint64_t latencyNanoseconds = getNanoseconds(args, 4);
699 if (sensorType == SensorType::STATIONARY_DETECT ||
700 sensorType == SensorType::INSTANT_MOTION_DETECT) {
701 intervalNanoseconds = kUint64Max;
702 latencyNanoseconds = 0;
703 }
704 fbb.Finish(ptest::CreateSensorRequestMessage(
705 fbb, enable, sensorType, intervalNanoseconds, latencyNanoseconds));
706 LOGI(
707 "Created SensorRequestMessage, enable %d, %s sensor, sampling "
708 "interval ns %" PRIu64 ", latency ns %" PRIu64,
709 enable, ptest::EnumNameSensorType(sensorType), intervalNanoseconds,
710 latencyNanoseconds);
711 }
712
createBreakItMessage(FlatBufferBuilder & fbb,std::vector<string> & args)713 void createBreakItMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
714 bool enable = (args[1] == "enable");
715 fbb.Finish(ptest::CreateBreakItMessage(fbb, enable));
716 LOGI("Created BreakItMessage, enable %d", enable);
717 }
718
createGnssMeasMessage(FlatBufferBuilder & fbb,std::vector<string> & args)719 void createGnssMeasMessage(FlatBufferBuilder &fbb, std::vector<string> &args) {
720 bool enable = (args[1] == "enable");
721 uint32_t intervalMilliseconds = getMilliseconds(args, 2);
722 fbb.Finish(
723 ptest::CreateGnssMeasurementMessage(fbb, enable, intervalMilliseconds));
724 LOGI("Created GnssMeasurementMessage, enable %d, interval ms %" PRIu32,
725 enable, intervalMilliseconds);
726 }
727
sendMessageToNanoapp(SocketClient & client,sp<SocketCallbacks> callbacks,FlatBufferBuilder & fbb,uint64_t appId,MessageType messageType)728 bool sendMessageToNanoapp(SocketClient &client, sp<SocketCallbacks> callbacks,
729 FlatBufferBuilder &fbb, uint64_t appId,
730 MessageType messageType) {
731 FlatBufferBuilder builder(128);
732 HostProtocolHost::encodeNanoappMessage(
733 builder, appId, static_cast<uint32_t>(messageType), kHostEndpoint,
734 fbb.GetBufferPointer(), fbb.GetSize());
735 LOGI("sending %s message to nanoapp (%" PRIu32 " bytes w/%" PRIu32
736 " bytes of payload)",
737 ptest::EnumNameMessageType(messageType), builder.GetSize(),
738 fbb.GetSize());
739 if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
740 LOGE("Failed to send %s message", ptest::EnumNameMessageType(messageType));
741 return false;
742 }
743 auto status = kReadyCond.wait_for(kReadyCondLock, kTimeout);
744 bool success =
745 (status == std::cv_status::no_timeout && callbacks->actionSucceeded());
746 LOGI("Sent %s message to nanoapp success: %d",
747 ptest::EnumNameMessageType(messageType), success);
748 if (status == std::cv_status::timeout) {
749 LOGE("Sent %s message to nanoapp timeout",
750 ptest::EnumNameMessageType(messageType));
751 }
752 return success;
753 }
754
usage()755 static void usage() {
756 LOGI(
757 "\n"
758 "Usage:\n"
759 " chre_power_test_client load <optional: tcm> <optional: path>\n"
760 " chre_power_test_client unload <optional: tcm>\n"
761 " chre_power_test_client unloadall\n"
762 " chre_power_test_client timer <optional: tcm> <enable> <interval_ns>\n"
763 " chre_power_test_client wifi <optional: tcm> <enable> <interval_ns>"
764 " <optional: wifi_scan_type> <optional: wifi_radio_chain>"
765 " <optional: wifi_channel_set>\n"
766 " chre_power_test_client gnss <optional: tcm> <enable> <interval_ms>"
767 " <next_fix_ms>\n"
768 " chre_power_test_client cell <optional: tcm> <enable> <interval_ns>\n"
769 " chre_power_test_client audio <optional: tcm> <enable> <duration_ns>\n"
770 " chre_power_test_client sensor <optional: tcm> <enable> <sensor_type>"
771 " <interval_ns> <optional: latency_ns>\n"
772 " chre_power_test_client breakit <optional: tcm> <enable>\n"
773 " chre_power_test_client gnss_meas <optional: tcm> <enable> <interval_ms>"
774 "\n"
775 "Command:\n"
776 "load: load power test nanoapp to CHRE\n"
777 "unload: unload power test nanoapp from CHRE\n"
778 "unloadall: unload all nanoapps in CHRE\n"
779 "timer: start/stop timer wake up\n"
780 "wifi: start/stop periodic wifi scan\n"
781 "gnss: start/stop periodic GPS scan\n"
782 "cell: start/stop periodic cellular scan\n"
783 "audio: start/stop periodic audio capture\n"
784 "sensor: start/stop periodic sensor sampling\n"
785 "breakit: start/stop all action for stress tests\n"
786 "gnss_meas: start/stop periodic GNSS measurement\n"
787 "\n"
788 "<optional: tcm>: tcm for micro image, default for big image\n"
789 "<enable>: enable/disable\n"
790 "\n"
791 "<sensor_type>:\n"
792 " accelerometer\n"
793 " instant_motion\n"
794 " stationary\n"
795 " gyroscope\n"
796 " uncalibrated_gyroscope\n"
797 " geomagnetic\n"
798 " uncalibrated_geomagnetic\n"
799 " pressure\n"
800 " light\n"
801 " proximity\n"
802 " step\n"
803 " uncalibrated_accelerometer\n"
804 " accelerometer_temperature\n"
805 " gyroscope_temperature\n"
806 " geomanetic_temperature\n"
807 "\n"
808 " For instant_montion and stationary sersor, it is not necessary to"
809 " provide the interval and latency.\n"
810 "\n"
811 "<wifi_scan_type>:\n"
812 " active\n"
813 " active_passive_dfs\n"
814 " passive\n"
815 " no_preference (default when omitted)\n"
816 "\n"
817 "<wifi_radio_chain>:\n"
818 " default (default when omitted)\n"
819 " low_latency\n"
820 " low_power\n"
821 " high_accuracy\n"
822 "\n"
823 "<wifi_channel_set>:\n"
824 " non_dfs (default when omitted)\n"
825 " all\n");
826 }
827
createRequestMessage(Command commandEnum,FlatBufferBuilder & fbb,std::vector<string> & args)828 void createRequestMessage(Command commandEnum, FlatBufferBuilder &fbb,
829 std::vector<string> &args) {
830 switch (commandEnum) {
831 case Command::kTimer: {
832 createTimerMessage(fbb, args);
833 break;
834 }
835 case Command::kWifi: {
836 createWifiMessage(fbb, args);
837 break;
838 }
839 case Command::kGnss: {
840 createGnssMessage(fbb, args);
841 break;
842 }
843 case Command::kCell: {
844 createCellMessage(fbb, args);
845 break;
846 }
847 case Command::kAudio: {
848 createAudioMessage(fbb, args);
849 break;
850 }
851 case Command::kSensor: {
852 createSensorMessage(fbb, args);
853 break;
854 }
855 case Command::kBreakIt: {
856 createBreakItMessage(fbb, args);
857 break;
858 }
859 case Command::kGnssMeas: {
860 createGnssMeasMessage(fbb, args);
861 break;
862 }
863 default: {
864 usage();
865 }
866 }
867 }
868
869 } // anonymous namespace
870
main(int argc,char * argv[])871 int main(int argc, char *argv[]) {
872 int argi = 0;
873 const std::string name{argv[argi++]};
874 const std::string cmd{argi < argc ? argv[argi++] : ""};
875
876 string commandLine(name);
877
878 if (commandMap.find(cmd) == commandMap.end()) {
879 usage();
880 return -1;
881 }
882
883 commandLine.append(" " + cmd);
884 Command commandEnum = commandMap[cmd];
885
886 std::vector<std::string> args;
887 while (argi < argc) {
888 args.push_back(std::string(argv[argi++]));
889 commandLine.append(" " + args.back());
890 }
891
892 LOGI("Command line: %s", commandLine.c_str());
893
894 if (!validateArguments(commandEnum, args)) {
895 LOGE("Invalid arguments");
896 usage();
897 return -1;
898 }
899
900 SocketClient client;
901 sp<SocketCallbacks> callbacks = new SocketCallbacks(kReadyCond);
902
903 if (!client.connect("chre", callbacks)) {
904 LOGE("Couldn't connect to socket");
905 return -1;
906 }
907
908 bool success = false;
909 switch (commandEnum) {
910 case Command::kUnloadAll: {
911 success = unloadAllNanoapps(client, callbacks);
912 break;
913 }
914 case Command::kUnload: {
915 success = unloadNanoapp(client, callbacks, getId(args));
916 break;
917 }
918 case Command::kLoad: {
919 LOGI("Loading nanoapp from %s", getPath(args).c_str());
920 success =
921 loadNanoapp(client, callbacks, getPath(args).c_str(), getId(args),
922 kAppVersion, kApiVersion, isTcmArgSpecified(args));
923 break;
924 }
925 default: {
926 if (!isLoaded(client, callbacks, args)) {
927 LOGE("The power test nanoapp has to be loaded before sending request");
928 return -1;
929 }
930 FlatBufferBuilder fbb(64);
931 createRequestMessage(commandEnum, fbb, args);
932 success = sendMessageToNanoapp(client, callbacks, fbb, getId(args),
933 messageTypeMap[cmd]);
934 }
935 }
936
937 client.disconnect();
938 return success ? 0 : -1;
939 }
940