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