1 /*
2  * Copyright (C) 2020 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 "wifi_pal_impl_test.h"
18 
19 #include "chre/platform/log.h"
20 #include "chre/platform/shared/pal_system_api.h"
21 #include "chre/platform/system_time.h"
22 #include "chre/util/lock_guard.h"
23 #include "chre/util/nanoapp/wifi.h"
24 
25 #include <cinttypes>
26 
27 // Flag to require on-demand WiFi scanning capability to be enabled for the test
28 // to pass. Set to false to allow tests to pass on disabled platforms.
29 #ifndef PAL_IMPL_TEST_WIFI_ON_DEMAND_SCAN_REQUIRED
30 #define PAL_IMPL_TEST_WIFI_ON_DEMAND_SCAN_REQUIRED true
31 #endif
32 
33 // Same as above for scan monitoring.
34 #ifndef PAL_IMPL_TEST_WIFI_SCAN_MONITORING_REQUIRED
35 #define PAL_IMPL_TEST_WIFI_SCAN_MONITORING_REQUIRED true
36 #endif
37 
38 namespace wifi_pal_impl_test {
39 
40 namespace {
41 
42 using ::chre::Nanoseconds;
43 using ::chre::Seconds;
44 using ::chre::SystemTime;
45 
46 //! A pointer to the current test running
47 wifi_pal_impl_test::PalWifiTest *gTest = nullptr;
48 
49 //! Timeout as specified by the CHRE API
50 const Nanoseconds kAsyncResultTimeoutNs =
51     Nanoseconds(CHRE_ASYNC_RESULT_TIMEOUT_NS);
52 const Nanoseconds kScanResultTimeoutNs =
53     Nanoseconds(CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS);
54 
chrePalScanMonitorStatusChangeCallback(bool enabled,uint8_t errorCode)55 void chrePalScanMonitorStatusChangeCallback(bool enabled, uint8_t errorCode) {
56   if (gTest != nullptr) {
57     gTest->scanMonitorStatusChangeCallback(enabled, errorCode);
58   }
59 }
60 
chrePalScanResponseCallback(bool pending,uint8_t errorCode)61 void chrePalScanResponseCallback(bool pending, uint8_t errorCode) {
62   if (gTest != nullptr) {
63     gTest->scanResponseCallback(pending, errorCode);
64   }
65 }
66 
chrePalScanEventCallback(struct chreWifiScanEvent * event)67 void chrePalScanEventCallback(struct chreWifiScanEvent *event) {
68   if (gTest != nullptr) {
69     gTest->scanEventCallback(event);
70   }
71 }
72 
chrePalRangingEventCallback(uint8_t errorCode,struct chreWifiRangingEvent * event)73 void chrePalRangingEventCallback(uint8_t errorCode,
74                                  struct chreWifiRangingEvent *event) {
75   if (gTest != nullptr) {
76     gTest->rangingEventCallback(errorCode, event);
77   }
78 }
79 
logChreWifiResult(const chreWifiScanResult & result)80 void logChreWifiResult(const chreWifiScanResult &result) {
81   const char *ssidStr = "<non-printable>";
82   char ssidBuffer[chre::kMaxSsidStrLen];
83   if (result.ssidLen == 0) {
84     ssidStr = "<empty>";
85   } else if (chre::parseSsidToStr(ssidBuffer, sizeof(ssidBuffer), result.ssid,
86                                   result.ssidLen)) {
87     ssidStr = ssidBuffer;
88   }
89 
90   LOGI("Found network with SSID: %s", ssidStr);
91   const char *bssidStr = "<non-printable>";
92   char bssidBuffer[chre::kBssidStrLen];
93   if (chre::parseBssidToStr(result.bssid, bssidBuffer, sizeof(bssidBuffer))) {
94     bssidStr = bssidBuffer;
95   }
96 
97   LOGI("  age (ms): %" PRIu32, result.ageMs);
98   LOGI("  capability info: 0x%" PRIx16, result.capabilityInfo);
99   LOGI("  bssid: %s", bssidStr);
100   LOGI("  flags: 0x%" PRIx8, result.flags);
101   LOGI("  rssi: %" PRId8 "dBm", result.rssi);
102   LOGI("  band: %s (%" PRIu8 ")", chre::parseChreWifiBand(result.band),
103        result.band);
104   LOGI("  primary channel: %" PRIu32, result.primaryChannel);
105   LOGI("  center frequency primary: %" PRIu32, result.centerFreqPrimary);
106   LOGI("  center frequency secondary: %" PRIu32, result.centerFreqSecondary);
107   LOGI("  channel width: %" PRIu8, result.channelWidth);
108   LOGI("  security mode: 0x%" PRIx8, result.securityMode);
109 }
110 
111 }  // anonymous namespace
112 
SetUp()113 void PalWifiTest::SetUp() {
114   api_ = chrePalWifiGetApi(CHRE_PAL_WIFI_API_CURRENT_VERSION);
115   ASSERT_NE(api_, nullptr);
116   EXPECT_EQ(api_->moduleVersion, CHRE_PAL_WIFI_API_CURRENT_VERSION);
117 
118   // Open the PAL API
119   static const struct chrePalWifiCallbacks kCallbacks = {
120       .scanMonitorStatusChangeCallback = chrePalScanMonitorStatusChangeCallback,
121       .scanResponseCallback = chrePalScanResponseCallback,
122       .scanEventCallback = chrePalScanEventCallback,
123       .rangingEventCallback = chrePalRangingEventCallback,
124   };
125   ASSERT_TRUE(api_->open(&chre::gChrePalSystemApi, &kCallbacks));
126   gTest = this;
127 
128   errorCode_ = CHRE_ERROR_LAST;
129   numScanResultCount_ = 0;
130   lastScanEventReceived_ = false;
131   scanEventList_.clear();
132   scanParams_.reset();
133   lastEventIndex_ = UINT8_MAX;
134   scanMonitorEnabled_ = false;
135 }
136 
TearDown()137 void PalWifiTest::TearDown() {
138   gTest = nullptr;
139   api_->close();
140 }
141 
scanMonitorStatusChangeCallback(bool enabled,uint8_t errorCode)142 void PalWifiTest::scanMonitorStatusChangeCallback(bool enabled,
143                                                   uint8_t errorCode) {
144   LOGI("Received scan monitor response with enabled %d error %" PRIu8, enabled,
145        errorCode);
146   if (errorCode == CHRE_ERROR_LAST) {
147     LOGE("Received CHRE_ERROR_LAST");
148     errorCode = CHRE_ERROR;
149   }
150   chre::LockGuard<chre::Mutex> lock(mutex_);
151   scanMonitorEnabled_ = enabled;
152   errorCode_ = errorCode;
153   condVar_.notify_one();
154 }
155 
scanResponseCallback(bool pending,uint8_t errorCode)156 void PalWifiTest::scanResponseCallback(bool pending, uint8_t errorCode) {
157   LOGI("Received scan response with pending %d error %" PRIu8, pending,
158        errorCode);
159   if (errorCode == CHRE_ERROR_LAST) {
160     LOGE("Received CHRE_ERROR_LAST");
161     errorCode = CHRE_ERROR;
162   }
163   chre::LockGuard<chre::Mutex> lock(mutex_);
164   errorCode_ = errorCode;
165   condVar_.notify_one();
166 }
167 
scanEventCallback(struct chreWifiScanEvent * event)168 void PalWifiTest::scanEventCallback(struct chreWifiScanEvent *event) {
169   if (event == nullptr) {
170     LOGE("Got null scan event");
171   } else {
172     {
173       chre::LockGuard<chre::Mutex> lock(mutex_);
174       scanEventList_.push_back(event);
175       numScanResultCount_ += event->resultCount;
176       lastScanEventReceived_ = (numScanResultCount_ == event->resultTotal);
177     }
178 
179     condVar_.notify_one();
180   }
181 }
182 
rangingEventCallback(uint8_t errorCode,struct chreWifiRangingEvent * event)183 void PalWifiTest::rangingEventCallback(uint8_t errorCode,
184                                        struct chreWifiRangingEvent *event) {
185   // TODO:
186 }
187 
validateWifiScanEvent(const chreWifiScanEvent & event)188 void PalWifiTest::validateWifiScanEvent(const chreWifiScanEvent &event) {
189   if (scanParams_.has_value()) {
190     EXPECT_EQ(event.scanType, scanParams_->scanType);
191     EXPECT_GE(event.referenceTime,
192               chreGetTime() - (scanParams_->maxScanAgeMs *
193                                chre::kOneMillisecondInNanoseconds));
194     EXPECT_EQ(event.radioChainPref, scanParams_->radioChainPref);
195     EXPECT_EQ(event.eventIndex, static_cast<uint8_t>(lastEventIndex_ + 1));
196   }
197 }
198 
waitForAsyncResponseAssertSuccess(chre::Nanoseconds timeoutNs)199 void PalWifiTest::waitForAsyncResponseAssertSuccess(
200     chre::Nanoseconds timeoutNs) {
201   bool waitSuccess = true;
202   while (errorCode_ == CHRE_ERROR_LAST && waitSuccess) {
203     waitSuccess = condVar_.wait_for(mutex_, timeoutNs);
204   }
205   ASSERT_TRUE(waitSuccess);
206   ASSERT_EQ(errorCode_, CHRE_ERROR_NONE);
207 }
208 
TEST_F(PalWifiTest,ScanAsyncTest)209 TEST_F(PalWifiTest, ScanAsyncTest) {
210   bool hasOnDemandScanCapability =
211       (api_->getCapabilities() & CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN) ==
212       CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN;
213 #if PAL_IMPL_TEST_WIFI_ON_DEMAND_SCAN_REQUIRED
214   ASSERT_TRUE(hasOnDemandScanCapability);
215 #else
216   if (!hasOnDemandScanCapability) {
217     GTEST_SKIP();
218   }
219 #endif
220 
221   // Request a WiFi scan
222   chre::LockGuard<chre::Mutex> lock(mutex_);
223 
224   struct chreWifiScanParams params = {};
225   params.scanType = CHRE_WIFI_SCAN_TYPE_ACTIVE;
226   params.maxScanAgeMs = 5000;  // 5 seconds
227   params.frequencyListLen = 0;
228   params.ssidListLen = 0;
229   params.radioChainPref = CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT;
230   scanParams_ = params;
231 
232   prepareForAsyncResponse();
233   ASSERT_TRUE(api_->requestScan(&scanParams_.value()));
234   waitForAsyncResponseAssertSuccess(kScanResultTimeoutNs);
235 
236   // The CHRE API only poses timeout requirements on the async response. Use
237   // the same timeout to receive the scan results to avoid blocking forever.
238   bool waitSuccess = true;
239   while (!lastScanEventReceived_ && waitSuccess) {
240     waitSuccess = condVar_.wait_for(mutex_, kScanResultTimeoutNs);
241   }
242 
243   for (auto *event : scanEventList_) {
244     for (uint8_t i = 0; i < event->resultCount; i++) {
245       const chreWifiScanResult &result = event->results[i];
246       logChreWifiResult(result);
247     }
248     validateWifiScanEvent(*event);
249 
250     lastEventIndex_ = event->eventIndex;
251     api_->releaseScanEvent(event);
252   }
253 
254   EXPECT_TRUE(lastScanEventReceived_);
255   EXPECT_GT(numScanResultCount_, 0);
256 }
257 
258 // Note: This test only verifies that the scan monitor succeeds according
259 // to the async response.
TEST_F(PalWifiTest,ScanMonitorTest)260 TEST_F(PalWifiTest, ScanMonitorTest) {
261   bool hasScanMonitoringCapability =
262       (api_->getCapabilities() & CHRE_WIFI_CAPABILITIES_SCAN_MONITORING) ==
263       CHRE_WIFI_CAPABILITIES_SCAN_MONITORING;
264 #if PAL_IMPL_TEST_WIFI_SCAN_MONITORING_REQUIRED
265   ASSERT_TRUE(hasScanMonitoringCapability);
266 #else
267   if (!hasScanMonitoringCapability) {
268     GTEST_SKIP();
269   }
270 #endif
271 
272   chre::LockGuard<chre::Mutex> lock(mutex_);
273 
274   prepareForAsyncResponse();
275   ASSERT_TRUE(api_->configureScanMonitor(true /* enable */));
276   waitForAsyncResponseAssertSuccess(kAsyncResultTimeoutNs);
277   ASSERT_TRUE(scanMonitorEnabled_);
278 
279   prepareForAsyncResponse();
280   ASSERT_TRUE(api_->configureScanMonitor(false /* enable */));
281   waitForAsyncResponseAssertSuccess(kAsyncResultTimeoutNs);
282   ASSERT_FALSE(scanMonitorEnabled_);
283 }
284 
285 }  // namespace wifi_pal_impl_test
286