• 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 //! 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