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