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