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