• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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