1 /*
2 * Copyright (C) 2022 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 <cstdint>
18
19 #include "chre/core/event_loop_manager.h"
20 #include "chre/core/settings.h"
21 #include "chre/platform/linux/pal_wifi.h"
22 #include "chre/platform/log.h"
23 #include "chre/util/nanoapp/app_id.h"
24 #include "chre/util/system/napp_permissions.h"
25 #include "chre_api/chre/event.h"
26 #include "chre_api/chre/re.h"
27 #include "chre_api/chre/wifi.h"
28 #include "gtest/gtest.h"
29 #include "test_base.h"
30 #include "test_event.h"
31 #include "test_event_queue.h"
32 #include "test_util.h"
33
34 namespace chre {
35 namespace {
36 // WifiTimeoutTestBase needs to set timeout more than the max waitForEvent()
37 // should process (Currently it is
38 // WifiCanDispatchSecondScanRequestInQueueAfterFirstTimeout). If not,
39 // waitForEvent will timeout before actual timeout happens in CHRE, making us
40 // unable to observe how system handles timeout.
41 class WifiTimeoutTestBase : public TestBase {
42 protected:
getTimeoutNs() const43 uint64_t getTimeoutNs() const override {
44 return 3 * CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS;
45 }
46 };
47
48 CREATE_CHRE_TEST_EVENT(SCAN_REQUEST, 20);
49 CREATE_CHRE_TEST_EVENT(REQUEST_TIMED_OUT, 21);
50
TEST_F(WifiTimeoutTestBase,WifiScanRequestTimeoutTest)51 TEST_F(WifiTimeoutTestBase, WifiScanRequestTimeoutTest) {
52 class ScanTestNanoapp : public TestNanoapp {
53 public:
54 explicit ScanTestNanoapp()
55 : TestNanoapp(
56 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {}
57
58 bool start() override {
59 mRequestTimer = CHRE_TIMER_INVALID;
60 return true;
61 }
62
63 void handleEvent(uint32_t, uint16_t eventType,
64 const void *eventData) override {
65 switch (eventType) {
66 case CHRE_EVENT_WIFI_ASYNC_RESULT: {
67 auto *event = static_cast<const chreAsyncResult *>(eventData);
68 if (mRequestTimer != CHRE_TIMER_INVALID) {
69 chreTimerCancel(mRequestTimer);
70 mRequestTimer = CHRE_TIMER_INVALID;
71 }
72 if (event->success) {
73 TestEventQueueSingleton::get()->pushEvent(
74 CHRE_EVENT_WIFI_ASYNC_RESULT,
75 *(static_cast<const uint32_t *>(event->cookie)));
76 }
77 break;
78 }
79
80 case CHRE_EVENT_WIFI_SCAN_RESULT: {
81 TestEventQueueSingleton::get()->pushEvent(
82 CHRE_EVENT_WIFI_SCAN_RESULT);
83 break;
84 }
85
86 case CHRE_EVENT_TIMER: {
87 TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT);
88 mRequestTimer = CHRE_TIMER_INVALID;
89 break;
90 }
91
92 case CHRE_EVENT_TEST_EVENT: {
93 auto event = static_cast<const TestEvent *>(eventData);
94 switch (event->type) {
95 case SCAN_REQUEST:
96 bool success = false;
97 mCookie = *static_cast<uint32_t *>(event->data);
98 if (chreWifiRequestScanAsyncDefault(&mCookie)) {
99 mRequestTimer =
100 chreTimerSet(CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS, nullptr,
101 true /* oneShot */);
102 success = mRequestTimer != CHRE_TIMER_INVALID;
103 }
104 TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success);
105 break;
106 }
107 break;
108 }
109 }
110 }
111
112 protected:
113 uint32_t mCookie;
114 uint32_t mRequestTimer;
115 };
116
117 uint64_t appId = loadNanoapp(MakeUnique<ScanTestNanoapp>());
118
119 constexpr uint32_t timeOutCookie = 0xdead;
120 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN,
121 false /* enableResponse */);
122 sendEventToNanoapp(appId, SCAN_REQUEST, timeOutCookie);
123 bool success;
124 waitForEvent(SCAN_REQUEST, &success);
125 EXPECT_TRUE(success);
126
127 waitForEvent(REQUEST_TIMED_OUT);
128
129 // Make sure that we can still request scan after a timedout
130 // request.
131 constexpr uint32_t successCookie = 0x0101;
132 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN,
133 true /* enableResponse */);
134 sendEventToNanoapp(appId, SCAN_REQUEST, successCookie);
135 waitForEvent(SCAN_REQUEST, &success);
136 EXPECT_TRUE(success);
137 waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
138
139 unloadNanoapp(appId);
140 }
141
TEST_F(WifiTimeoutTestBase,WifiCanDispatchQueuedRequestAfterOneTimeout)142 TEST_F(WifiTimeoutTestBase, WifiCanDispatchQueuedRequestAfterOneTimeout) {
143 constexpr uint8_t kNanoappNum = 2;
144 // receivedTimeout is shared across apps and must be static.
145 // But we want it initialized each time the test is executed.
146 static uint8_t receivedTimeout;
147 receivedTimeout = 0;
148
149 class ScanTestNanoapp : public TestNanoapp {
150 public:
151 explicit ScanTestNanoapp(uint64_t id = kDefaultTestNanoappId)
152 : TestNanoapp(TestNanoappInfo{
153 .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {}
154
155 bool start() override {
156 for (uint8_t i = 0; i < kNanoappNum; ++i) {
157 mRequestTimers[i] = CHRE_TIMER_INVALID;
158 }
159 return true;
160 }
161
162 void handleEvent(uint32_t, uint16_t eventType,
163 const void *eventData) override {
164 size_t index = id() - CHRE_VENDOR_ID_EXAMPLE - 1;
165 switch (eventType) {
166 case CHRE_EVENT_WIFI_ASYNC_RESULT: {
167 auto *event = static_cast<const chreAsyncResult *>(eventData);
168 if (mRequestTimers[index] != CHRE_TIMER_INVALID) {
169 chreTimerCancel(mRequestTimers[index]);
170 mRequestTimers[index] = CHRE_TIMER_INVALID;
171 }
172 if (event->success) {
173 TestEventQueueSingleton::get()->pushEvent(
174 CHRE_EVENT_WIFI_ASYNC_RESULT,
175 *(static_cast<const uint32_t *>(event->cookie)));
176 }
177 break;
178 }
179
180 case CHRE_EVENT_WIFI_SCAN_RESULT: {
181 TestEventQueueSingleton::get()->pushEvent(
182 CHRE_EVENT_WIFI_SCAN_RESULT);
183 break;
184 }
185
186 case CHRE_EVENT_TIMER: {
187 if (eventData == &mCookie[index]) {
188 receivedTimeout++;
189 mRequestTimers[index] = CHRE_TIMER_INVALID;
190 }
191 if (receivedTimeout == 2) {
192 TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT);
193 }
194 break;
195 }
196
197 case CHRE_EVENT_TEST_EVENT: {
198 auto event = static_cast<const TestEvent *>(eventData);
199 switch (event->type) {
200 case SCAN_REQUEST:
201 bool success = false;
202 mCookie[index] = *static_cast<uint32_t *>(event->data);
203 if (chreWifiRequestScanAsyncDefault(&mCookie[index])) {
204 mRequestTimers[index] =
205 chreTimerSet(CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS,
206 &mCookie[index], true /* oneShot */);
207 success = mRequestTimers[index] != CHRE_TIMER_INVALID;
208 }
209 TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success);
210 break;
211 }
212 break;
213 }
214 }
215 }
216
217 protected:
218 uint32_t mCookie[kNanoappNum];
219 uint32_t mRequestTimers[kNanoappNum];
220 };
221 constexpr uint64_t kAppOneId = makeExampleNanoappId(1);
222 constexpr uint64_t kAppTwoId = makeExampleNanoappId(2);
223
224 uint64_t firstAppId = loadNanoapp(MakeUnique<ScanTestNanoapp>(kAppOneId));
225 uint64_t secondAppId = loadNanoapp(MakeUnique<ScanTestNanoapp>(kAppTwoId));
226
227 constexpr uint32_t timeOutCookie = 0xdead;
228 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN,
229 false /* enableResponse */);
230 bool success;
231 sendEventToNanoapp(firstAppId, SCAN_REQUEST, timeOutCookie);
232 waitForEvent(SCAN_REQUEST, &success);
233 EXPECT_TRUE(success);
234 sendEventToNanoapp(secondAppId, SCAN_REQUEST, timeOutCookie);
235 waitForEvent(SCAN_REQUEST, &success);
236 EXPECT_TRUE(success);
237
238 waitForEvent(REQUEST_TIMED_OUT);
239
240 // Make sure that we can still request scan for both nanoapps after a timedout
241 // request.
242 constexpr uint32_t successCookie = 0x0101;
243 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN,
244 true /* enableResponse */);
245 sendEventToNanoapp(firstAppId, SCAN_REQUEST, successCookie);
246 waitForEvent(SCAN_REQUEST, &success);
247 EXPECT_TRUE(success);
248 waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
249 sendEventToNanoapp(secondAppId, SCAN_REQUEST, successCookie);
250 waitForEvent(SCAN_REQUEST, &success);
251 EXPECT_TRUE(success);
252 waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
253
254 unloadNanoapp(firstAppId);
255 unloadNanoapp(secondAppId);
256 }
257
TEST_F(WifiTimeoutTestBase,WifiScanMonitorTimeoutTest)258 TEST_F(WifiTimeoutTestBase, WifiScanMonitorTimeoutTest) {
259 CREATE_CHRE_TEST_EVENT(SCAN_MONITOR_REQUEST, 1);
260
261 struct MonitoringRequest {
262 bool enable;
263 uint32_t cookie;
264 };
265
266 class App : public TestNanoapp {
267 public:
268 App()
269 : TestNanoapp(
270 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {}
271
272 bool start() override {
273 mRequestTimer = CHRE_TIMER_INVALID;
274 return true;
275 }
276
277 void handleEvent(uint32_t, uint16_t eventType,
278 const void *eventData) override {
279 switch (eventType) {
280 case CHRE_EVENT_WIFI_ASYNC_RESULT: {
281 auto *event = static_cast<const chreAsyncResult *>(eventData);
282 if (event->success) {
283 if (mRequestTimer != CHRE_TIMER_INVALID) {
284 chreTimerCancel(mRequestTimer);
285 mRequestTimer = CHRE_TIMER_INVALID;
286 }
287 TestEventQueueSingleton::get()->pushEvent(
288 CHRE_EVENT_WIFI_ASYNC_RESULT,
289 *(static_cast<const uint32_t *>(event->cookie)));
290 }
291 break;
292 }
293
294 case CHRE_EVENT_TIMER: {
295 mRequestTimer = CHRE_TIMER_INVALID;
296 TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT);
297 break;
298 }
299
300 case CHRE_EVENT_TEST_EVENT: {
301 auto event = static_cast<const TestEvent *>(eventData);
302 switch (event->type) {
303 case SCAN_MONITOR_REQUEST:
304 bool success = false;
305 auto request =
306 static_cast<const MonitoringRequest *>(event->data);
307 if (chreWifiConfigureScanMonitorAsync(request->enable,
308 &mCookie)) {
309 mCookie = request->cookie;
310 mRequestTimer = chreTimerSet(CHRE_TEST_ASYNC_RESULT_TIMEOUT_NS,
311 nullptr, true /* oneShot */);
312 success = mRequestTimer != CHRE_TIMER_INVALID;
313 }
314
315 TestEventQueueSingleton::get()->pushEvent(SCAN_MONITOR_REQUEST,
316 success);
317 }
318 }
319 }
320 }
321
322 protected:
323 uint32_t mCookie;
324 uint32_t mRequestTimer;
325 };
326
327 uint64_t appId = loadNanoapp(MakeUnique<App>());
328
329 MonitoringRequest timeoutRequest{.enable = true, .cookie = 0xdead};
330 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN_MONITORING, false);
331 sendEventToNanoapp(appId, SCAN_MONITOR_REQUEST, timeoutRequest);
332 bool success;
333 waitForEvent(SCAN_MONITOR_REQUEST, &success);
334 EXPECT_TRUE(success);
335
336 waitForEvent(REQUEST_TIMED_OUT);
337
338 // Make sure that we can still request to change scan monitor after a timedout
339 // request.
340 MonitoringRequest enableRequest{.enable = true, .cookie = 0x1010};
341 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::SCAN_MONITORING, true);
342 sendEventToNanoapp(appId, SCAN_MONITOR_REQUEST, enableRequest);
343 waitForEvent(SCAN_MONITOR_REQUEST, &success);
344 EXPECT_TRUE(success);
345
346 uint32_t cookie;
347 waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie);
348 EXPECT_EQ(cookie, enableRequest.cookie);
349 EXPECT_TRUE(chrePalWifiIsScanMonitoringActive());
350
351 MonitoringRequest disableRequest{.enable = false, .cookie = 0x0101};
352 sendEventToNanoapp(appId, SCAN_MONITOR_REQUEST, disableRequest);
353 waitForEvent(SCAN_MONITOR_REQUEST, &success);
354 EXPECT_TRUE(success);
355
356 waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie);
357 EXPECT_EQ(cookie, disableRequest.cookie);
358 EXPECT_FALSE(chrePalWifiIsScanMonitoringActive());
359
360 unloadNanoapp(appId);
361 }
362
TEST_F(WifiTimeoutTestBase,WifiRequestRangingTimeoutTest)363 TEST_F(WifiTimeoutTestBase, WifiRequestRangingTimeoutTest) {
364 CREATE_CHRE_TEST_EVENT(RANGING_REQUEST, 0);
365
366 class App : public TestNanoapp {
367 public:
368 App()
369 : TestNanoapp(
370 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {}
371
372 bool start() override {
373 mRequestTimer = CHRE_TIMER_INVALID;
374 return true;
375 }
376
377 void handleEvent(uint32_t, uint16_t eventType,
378 const void *eventData) override {
379 switch (eventType) {
380 case CHRE_EVENT_WIFI_ASYNC_RESULT: {
381 if (mRequestTimer != CHRE_TIMER_INVALID) {
382 chreTimerCancel(mRequestTimer);
383 mRequestTimer = CHRE_TIMER_INVALID;
384 }
385
386 auto *event = static_cast<const chreAsyncResult *>(eventData);
387 if (event->success) {
388 if (event->errorCode == 0) {
389 TestEventQueueSingleton::get()->pushEvent(
390 CHRE_EVENT_WIFI_ASYNC_RESULT,
391 *(static_cast<const uint32_t *>(event->cookie)));
392 }
393 }
394 break;
395 }
396
397 case CHRE_EVENT_TIMER: {
398 mRequestTimer = CHRE_TIMER_INVALID;
399 TestEventQueueSingleton::get()->pushEvent(REQUEST_TIMED_OUT);
400 break;
401 }
402
403 case CHRE_EVENT_TEST_EVENT: {
404 auto event = static_cast<const TestEvent *>(eventData);
405 switch (event->type) {
406 case RANGING_REQUEST:
407 bool success = false;
408 mCookie = *static_cast<uint32_t *>(event->data);
409
410 // Placeholder parameters since linux PAL does not use this to
411 // generate response
412 struct chreWifiRangingTarget dummyRangingTarget = {
413 .macAddress = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
414 .primaryChannel = 0xdef02468,
415 .centerFreqPrimary = 0xace13579,
416 .centerFreqSecondary = 0xbdf369cf,
417 .channelWidth = 0x48,
418 };
419
420 struct chreWifiRangingParams dummyRangingParams = {
421 .targetListLen = 1,
422 .targetList = &dummyRangingTarget,
423 };
424
425 if (chreWifiRequestRangingAsync(&dummyRangingParams, &mCookie)) {
426 mRequestTimer =
427 chreTimerSet(CHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS,
428 nullptr, true /* oneShot */);
429 success = mRequestTimer != CHRE_TIMER_INVALID;
430 }
431 TestEventQueueSingleton::get()->pushEvent(RANGING_REQUEST,
432 success);
433 }
434 }
435 }
436 }
437
438 protected:
439 uint32_t mCookie;
440 uint32_t mRequestTimer;
441 };
442
443 uint64_t appId = loadNanoapp(MakeUnique<App>());
444
445 uint32_t timeOutCookie = 0xdead;
446
447 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::RANGING, false);
448 sendEventToNanoapp(appId, RANGING_REQUEST, timeOutCookie);
449 bool success;
450 waitForEvent(RANGING_REQUEST, &success);
451 EXPECT_TRUE(success);
452
453 waitForEvent(REQUEST_TIMED_OUT);
454
455 // Make sure that we can still request ranging after a timedout request
456 uint32_t successCookie = 0x0101;
457 chrePalWifiEnableResponse(PalWifiAsyncRequestTypes::RANGING, true);
458 sendEventToNanoapp(appId, RANGING_REQUEST, successCookie);
459 waitForEvent(RANGING_REQUEST, &success);
460 EXPECT_TRUE(success);
461
462 uint32_t cookie;
463 waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &cookie);
464 EXPECT_EQ(cookie, successCookie);
465
466 unloadNanoapp(appId);
467 }
468
469 } // namespace
470 } // namespace chre
471