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 "chre_settings_test_manager.h"
18
19 #include <pb_decode.h>
20 #include <pb_encode.h>
21
22 #include "chre/util/macros.h"
23 #include "chre/util/nanoapp/ble.h"
24 #include "chre/util/nanoapp/callbacks.h"
25 #include "chre/util/nanoapp/log.h"
26 #include "chre/util/time.h"
27 #include "chre_settings_test.nanopb.h"
28 #include "send_message.h"
29
30 #define LOG_TAG "[ChreSettingsTest]"
31
32 using chre::createBleScanFilterForKnownBeacons;
33 using chre::ble_constants::kNumScanFilters;
34
35 namespace chre {
36
37 namespace settings_test {
38
39 namespace {
40
41 constexpr uint32_t kWifiScanningCookie = 0x1234;
42 constexpr uint32_t kWifiRttCookie = 0x2345;
43 constexpr uint32_t kGnssLocationCookie = 0x3456;
44 constexpr uint32_t kGnssMeasurementCookie = 0x4567;
45 constexpr uint32_t kWwanCellInfoCookie = 0x5678;
46
47 // The default audio handle.
48 constexpr uint32_t kAudioHandle = 0;
49
50 // Flag to verify if an audio data event was received after a valid sampling
51 // change event (i.e., we only got the data event after a source-enabled-and-
52 // not-suspended event).
53 bool gGotSourceEnabledEvent = false;
54
55 uint32_t gAudioDataTimerHandle = CHRE_TIMER_INVALID;
56 constexpr uint32_t kAudioDataTimerCookie = 0xc001cafe;
57 uint32_t gAudioStatusTimerHandle = CHRE_TIMER_INVALID;
58 constexpr uint32_t kAudioStatusTimerCookie = 0xb01dcafe;
59 uint32_t gRangingRequestRetryTimerHandle = CHRE_TIMER_INVALID;
60 constexpr uint32_t kRangingRequestSetupRetryTimerCookie = 0x600ccafe;
61 constexpr uint32_t kRangingRequestRetryTimerCookie = 0x600dcafe;
62 uint32_t gWwanRequestRetryTimerHandle = CHRE_TIMER_INVALID;
63 constexpr uint32_t kWwanRequestRetryTimerCookie = 0x01d3cafe;
64
65 constexpr uint8_t kMaxWwanRequestRetries = 3;
66 constexpr uint8_t kMaxWifiRequestRetries = 3;
67
getFeature(const chre_settings_test_TestCommand & command,Manager::Feature * feature)68 bool getFeature(const chre_settings_test_TestCommand &command,
69 Manager::Feature *feature) {
70 bool success = true;
71 switch (command.feature) {
72 case chre_settings_test_TestCommand_Feature_WIFI_SCANNING:
73 *feature = Manager::Feature::WIFI_SCANNING;
74 break;
75 case chre_settings_test_TestCommand_Feature_WIFI_RTT:
76 *feature = Manager::Feature::WIFI_RTT;
77 break;
78 case chre_settings_test_TestCommand_Feature_GNSS_LOCATION:
79 *feature = Manager::Feature::GNSS_LOCATION;
80 break;
81 case chre_settings_test_TestCommand_Feature_GNSS_MEASUREMENT:
82 *feature = Manager::Feature::GNSS_MEASUREMENT;
83 break;
84 case chre_settings_test_TestCommand_Feature_WWAN_CELL_INFO:
85 *feature = Manager::Feature::WWAN_CELL_INFO;
86 break;
87 case chre_settings_test_TestCommand_Feature_AUDIO:
88 *feature = Manager::Feature::AUDIO;
89 break;
90 case chre_settings_test_TestCommand_Feature_BLE_SCANNING:
91 *feature = Manager::Feature::BLE_SCANNING;
92 break;
93 default:
94 LOGE("Unknown feature %d", command.feature);
95 success = false;
96 }
97
98 return success;
99 }
100
getFeatureState(const chre_settings_test_TestCommand & command,Manager::FeatureState * state)101 bool getFeatureState(const chre_settings_test_TestCommand &command,
102 Manager::FeatureState *state) {
103 bool success = true;
104 switch (command.state) {
105 case chre_settings_test_TestCommand_State_ENABLED:
106 *state = Manager::FeatureState::ENABLED;
107 break;
108 case chre_settings_test_TestCommand_State_DISABLED:
109 *state = Manager::FeatureState::DISABLED;
110 break;
111 default:
112 LOGE("Unknown feature state %d", command.state);
113 success = false;
114 }
115
116 return success;
117 }
118
getTestStep(const chre_settings_test_TestCommand & command,Manager::TestStep * step)119 bool getTestStep(const chre_settings_test_TestCommand &command,
120 Manager::TestStep *step) {
121 bool success = true;
122 switch (command.step) {
123 case chre_settings_test_TestCommand_Step_SETUP:
124 *step = Manager::TestStep::SETUP;
125 break;
126 case chre_settings_test_TestCommand_Step_START:
127 *step = Manager::TestStep::START;
128 break;
129 default:
130 LOGE("Unknown test step %d", command.step);
131 success = false;
132 }
133
134 return success;
135 }
136
isTestSupported()137 bool isTestSupported() {
138 // CHRE settings requirements were introduced in CHRE v1.4
139 return chreGetVersion() >= CHRE_API_VERSION_1_4;
140 }
141
142 } // anonymous namespace
143
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)144 void Manager::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
145 const void *eventData) {
146 if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) {
147 handleMessageFromHost(
148 senderInstanceId,
149 static_cast<const chreMessageFromHostData *>(eventData));
150 } else if (senderInstanceId == CHRE_INSTANCE_ID) {
151 handleDataFromChre(eventType, eventData);
152 } else {
153 LOGW("Got unknown event type from senderInstanceId %" PRIu32
154 " and with eventType %" PRIu16,
155 senderInstanceId, eventType);
156 }
157 }
158
isFeatureSupported(Feature feature)159 bool Manager::isFeatureSupported(Feature feature) {
160 bool supported = false;
161
162 uint32_t version = chreGetVersion();
163 switch (feature) {
164 case Feature::WIFI_SCANNING: {
165 uint32_t capabilities = chreWifiGetCapabilities();
166 supported = (version >= CHRE_API_VERSION_1_1) &&
167 ((capabilities & CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN) != 0);
168 break;
169 }
170 case Feature::WIFI_RTT: {
171 uint32_t capabilities = chreWifiGetCapabilities();
172 supported = (version >= CHRE_API_VERSION_1_2) &&
173 ((capabilities & CHRE_WIFI_CAPABILITIES_RTT_RANGING) != 0);
174 break;
175 }
176 case Feature::GNSS_LOCATION: {
177 uint32_t capabilities = chreGnssGetCapabilities();
178 supported = (version >= CHRE_API_VERSION_1_1) &&
179 ((capabilities & CHRE_GNSS_CAPABILITIES_LOCATION) != 0);
180 break;
181 }
182 case Feature::GNSS_MEASUREMENT: {
183 uint32_t capabilities = chreGnssGetCapabilities();
184 supported = (version >= CHRE_API_VERSION_1_1) &&
185 ((capabilities & CHRE_GNSS_CAPABILITIES_MEASUREMENTS) != 0);
186 break;
187 }
188 case Feature::WWAN_CELL_INFO: {
189 uint32_t capabilities = chreWwanGetCapabilities();
190 supported = (version >= CHRE_API_VERSION_1_1) &&
191 ((capabilities & CHRE_WWAN_GET_CELL_INFO) != 0);
192 break;
193 }
194 case Feature::AUDIO: {
195 struct chreAudioSource source;
196 supported = chreAudioGetSource(kAudioHandle, &source);
197 break;
198 }
199 case Feature::BLE_SCANNING: {
200 uint32_t capabilities = chreBleGetCapabilities();
201 supported = (version >= CHRE_API_VERSION_1_7) &&
202 ((capabilities & CHRE_BLE_CAPABILITIES_SCAN) != 0);
203 break;
204 }
205 default:
206 LOGE("Unknown feature %" PRIu8, static_cast<uint8_t>(feature));
207 }
208
209 return supported;
210 }
211
handleMessageFromHost(uint32_t senderInstanceId,const chreMessageFromHostData * hostData)212 void Manager::handleMessageFromHost(uint32_t senderInstanceId,
213 const chreMessageFromHostData *hostData) {
214 bool success = false;
215 uint32_t messageType = hostData->messageType;
216 if (senderInstanceId != CHRE_INSTANCE_ID) {
217 LOGE("Incorrect sender instance id: %" PRIu32, senderInstanceId);
218 } else if (messageType != chre_settings_test_MessageType_TEST_COMMAND) {
219 LOGE("Invalid message type %" PRIu32, messageType);
220 } else {
221 pb_istream_t istream = pb_istream_from_buffer(
222 static_cast<const pb_byte_t *>(hostData->message),
223 hostData->messageSize);
224 chre_settings_test_TestCommand testCommand =
225 chre_settings_test_TestCommand_init_default;
226
227 if (!pb_decode(&istream, chre_settings_test_TestCommand_fields,
228 &testCommand)) {
229 LOGE("Failed to decode start command error %s", PB_GET_ERROR(&istream));
230 } else {
231 Feature feature;
232 FeatureState state;
233 TestStep step;
234 if (getFeature(testCommand, &feature) &&
235 getFeatureState(testCommand, &state) &&
236 getTestStep(testCommand, &step)) {
237 LOGD("starting test: feature: %u, state %u, step %u",
238 static_cast<uint8_t>(feature), static_cast<uint8_t>(state),
239 static_cast<uint8_t>(step));
240 handleStartTestMessage(hostData->hostEndpoint, feature, state, step);
241 success = true;
242 }
243 }
244 }
245 if (!success) {
246 test_shared::sendTestResultToHost(
247 hostData->hostEndpoint, chre_settings_test_MessageType_TEST_RESULT,
248 /*success=*/false, /*abortOnFailure=*/false);
249 }
250 }
251
handleStartTestMessage(uint16_t hostEndpointId,Feature feature,FeatureState state,TestStep step)252 void Manager::handleStartTestMessage(uint16_t hostEndpointId, Feature feature,
253 FeatureState state, TestStep step) {
254 // If the test/feature is not supported, treat as success and skip the test.
255 if (!isTestSupported() || !isFeatureSupported(feature)) {
256 LOGW("Skipping test - TestSupported: %u, FeatureSupported: %u",
257 isTestSupported(), isFeatureSupported(feature));
258 sendTestResult(hostEndpointId, /*success=*/true);
259 } else {
260 bool success = false;
261 if (step == TestStep::SETUP) {
262 if (feature != Feature::WIFI_RTT) {
263 LOGE("Unexpected feature %" PRIu8 " for test step",
264 static_cast<uint8_t>(feature));
265 } else {
266 success = chreWifiRequestScanAsyncDefault(&kWifiScanningCookie);
267 }
268 } else {
269 success = startTestForFeature(feature);
270 }
271
272 if (!success) {
273 sendTestResult(hostEndpointId, /*success=*/false);
274 } else {
275 mTestSession = TestSession(hostEndpointId, feature, state, step);
276 }
277 }
278 }
279
handleDataFromChre(uint16_t eventType,const void * eventData)280 void Manager::handleDataFromChre(uint16_t eventType, const void *eventData) {
281 if (mTestSession.has_value()) {
282 // The validation for the correct data w.r.t. the current test session
283 // will be done in the methods called from here.
284 switch (eventType) {
285 case CHRE_EVENT_AUDIO_DATA:
286 handleAudioDataEvent(
287 static_cast<const struct chreAudioDataEvent *>(eventData));
288 break;
289
290 case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
291 handleAudioSourceStatusEvent(
292 static_cast<const struct chreAudioSourceStatusEvent *>(eventData));
293 break;
294
295 case CHRE_EVENT_TIMER:
296 handleTimerEvent(eventData);
297 break;
298
299 case CHRE_EVENT_WIFI_ASYNC_RESULT:
300 handleWifiAsyncResult(static_cast<const chreAsyncResult *>(eventData));
301 break;
302
303 case CHRE_EVENT_WIFI_SCAN_RESULT:
304 handleWifiScanResult(static_cast<const chreWifiScanEvent *>(eventData));
305 break;
306
307 case CHRE_EVENT_GNSS_ASYNC_RESULT:
308 handleGnssAsyncResult(static_cast<const chreAsyncResult *>(eventData));
309 break;
310
311 case CHRE_EVENT_WWAN_CELL_INFO_RESULT:
312 handleWwanCellInfoResult(
313 static_cast<const chreWwanCellInfoResult *>(eventData));
314 break;
315
316 case CHRE_EVENT_BLE_ASYNC_RESULT:
317 handleBleAsyncResult(static_cast<const chreAsyncResult *>(eventData));
318 break;
319
320 default:
321 LOGE("Unknown event type %" PRIu16, eventType);
322 }
323 }
324 }
325
requestRangingForFeatureWifiRtt()326 bool Manager::requestRangingForFeatureWifiRtt() {
327 if (!mCachedRangingTarget.has_value()) {
328 LOGE("No cached WiFi RTT ranging target");
329 return false;
330 }
331 struct chreWifiRangingParams params = {
332 .targetListLen = 1, .targetList = &mCachedRangingTarget.value()};
333 return chreWifiRequestRangingAsync(¶ms, &kWifiRttCookie);
334 }
335
startTestForFeature(Feature feature)336 bool Manager::startTestForFeature(Feature feature) {
337 bool success = true;
338 switch (feature) {
339 case Feature::WIFI_SCANNING:
340 success = chreWifiRequestScanAsyncDefault(&kWifiScanningCookie);
341 break;
342
343 case Feature::WIFI_RTT: {
344 mWifiRequestRetries = 0;
345 success = requestRangingForFeatureWifiRtt();
346 break;
347 }
348
349 case Feature::GNSS_LOCATION:
350 success = chreGnssLocationSessionStartAsync(/*minIntervalMs=*/1000,
351 /*minTimeToNextFixMs=*/0,
352 &kGnssLocationCookie);
353 break;
354
355 case Feature::GNSS_MEASUREMENT:
356 success = chreGnssMeasurementSessionStartAsync(/*minIntervalMs=*/1000,
357 &kGnssMeasurementCookie);
358 break;
359
360 case Feature::WWAN_CELL_INFO:
361 mWwanRequestRetries = 0;
362 success = chreWwanGetCellInfoAsync(&kWwanCellInfoCookie);
363 break;
364
365 case Feature::AUDIO: {
366 struct chreAudioSource source;
367 if ((success = chreAudioGetSource(kAudioHandle, &source))) {
368 success = chreAudioConfigureSource(kAudioHandle, /*enable=*/true,
369 source.minBufferDuration,
370 source.minBufferDuration);
371 }
372 break;
373 }
374
375 case Feature::BLE_SCANNING: {
376 struct chreBleScanFilter filter;
377 chreBleGenericFilter uuidFilters[kNumScanFilters];
378 createBleScanFilterForKnownBeacons(filter, uuidFilters, kNumScanFilters);
379 success = chreBleStartScanAsync(/*mode=*/CHRE_BLE_SCAN_MODE_FOREGROUND,
380 /*reportDelayMs=*/0, &filter);
381 break;
382 }
383
384 default:
385 LOGE("Unknown feature %" PRIu8, static_cast<uint8_t>(feature));
386 return false;
387 }
388
389 if (!success) {
390 LOGE("Failed to make request for test feature %" PRIu8,
391 static_cast<uint8_t>(feature));
392 } else {
393 LOGI("Starting test for feature %" PRIu8, static_cast<uint8_t>(feature));
394 }
395
396 return success;
397 }
398
validateAsyncResult(const chreAsyncResult * result,const void * expectedCookie)399 bool Manager::validateAsyncResult(const chreAsyncResult *result,
400 const void *expectedCookie) {
401 bool success = false;
402 if (result->cookie != expectedCookie) {
403 LOGE("Unexpected cookie on async result");
404 } else {
405 bool featureEnabled = (mTestSession->featureState == FeatureState::ENABLED);
406 bool disabledErrorCode =
407 (result->errorCode == CHRE_ERROR_FUNCTION_DISABLED);
408
409 if (featureEnabled && disabledErrorCode) {
410 LOGE("Got disabled error code when feature is enabled");
411 } else if (!featureEnabled && !disabledErrorCode) {
412 LOGE("Got non-disabled error code when feature is disabled");
413 } else {
414 success = true;
415 }
416 }
417
418 return success;
419 }
420
handleWifiAsyncResult(const chreAsyncResult * result)421 void Manager::handleWifiAsyncResult(const chreAsyncResult *result) {
422 bool success = false;
423 uint8_t feature = static_cast<uint8_t>(mTestSession->feature);
424 switch (result->requestType) {
425 case CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN: {
426 if (mTestSession->feature == Feature::WIFI_RTT) {
427 if (result->errorCode == CHRE_ERROR ||
428 result->errorCode == CHRE_ERROR_BUSY) {
429 if (mWifiRequestRetries >= kMaxWifiRequestRetries) {
430 // The request has failed repeatedly and we are no longer retrying
431 // Return success=false to the host rather than timeout.
432 LOGE("Reached max wifi request retries: test feature %" PRIu8
433 ". Num retries=%" PRIu8,
434 feature, kMaxWifiRequestRetries);
435 break;
436 }
437
438 // Retry on CHRE_ERROR/CHRE_ERROR_BUSY after a short delay
439 mWifiRequestRetries++;
440 uint64_t delay = kOneSecondInNanoseconds * 2;
441 const uint32_t *cookie = mTestSession->step == TestStep::SETUP
442 ? &kRangingRequestSetupRetryTimerCookie
443 : &kRangingRequestRetryTimerCookie;
444 gRangingRequestRetryTimerHandle =
445 chreTimerSet(delay, cookie, /*oneShot=*/true);
446 LOGW(
447 "Request failed during %s step. Retrying "
448 "after delay=%" PRIu64 "ns, num_retries=%" PRIu8 "/%" PRIu8,
449 mTestSession->step == TestStep::SETUP ? "SETUP" : "START", delay,
450 mWifiRequestRetries, kMaxWifiRequestRetries);
451 return;
452 }
453
454 if (result->errorCode == CHRE_ERROR_NONE) {
455 // Ignore validating the scan async response since we only care about
456 // the actual scan event to initiate the RTT request.
457 return;
458 } else {
459 LOGE("Unexpected error in async result: test feature: %" PRIu8
460 " error: %" PRIu8,
461 feature, static_cast<uint8_t>(result->errorCode));
462 break;
463 }
464 }
465 if (mTestSession->feature != Feature::WIFI_SCANNING) {
466 LOGE("Unexpected WiFi scan async result: test feature %" PRIu8,
467 feature);
468 } else {
469 success = validateAsyncResult(
470 result, static_cast<const void *>(&kWifiScanningCookie));
471 }
472 break;
473 }
474 case CHRE_WIFI_REQUEST_TYPE_RANGING: {
475 if (mTestSession->feature != Feature::WIFI_RTT) {
476 LOGE("Unexpected WiFi ranging async result: test feature %" PRIu8,
477 feature);
478 } else {
479 success = validateAsyncResult(
480 result, static_cast<const void *>(&kWifiRttCookie));
481 }
482 break;
483 }
484 default:
485 LOGE("Unexpected WiFi request type %" PRIu8, result->requestType);
486 }
487
488 sendTestResult(mTestSession->hostEndpointId, success);
489 }
490
handleWifiScanResult(const chreWifiScanEvent * result)491 void Manager::handleWifiScanResult(const chreWifiScanEvent *result) {
492 if (mTestSession->feature == Feature::WIFI_RTT &&
493 mTestSession->step == TestStep::SETUP) {
494 if (result->resultCount == 0) {
495 LOGE("Received empty WiFi scan result");
496 sendTestResult(mTestSession->hostEndpointId, /*success=*/false);
497 } else {
498 mReceivedScanResults += result->resultCount;
499 chreWifiRangingTarget target;
500 // Try to find an AP with the FTM responder flag set. The RTT ranging
501 // request should still work equivalently even if the flag is not set (but
502 // possibly with an error in the ranging result), so we use the last entry
503 // if none is found.
504 size_t index = result->resultCount - 1;
505 for (uint8_t i = 0; i < result->resultCount - 1; i++) {
506 if ((result->results[i].flags &
507 CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER) != 0) {
508 index = i;
509 break;
510 }
511 }
512 chreWifiRangingTargetFromScanResult(&result->results[index], &target);
513 mCachedRangingTarget = target;
514 if (result->resultTotal == mReceivedScanResults) {
515 mReceivedScanResults = 0;
516 test_shared::sendEmptyMessageToHost(
517 mTestSession->hostEndpointId,
518 chre_settings_test_MessageType_TEST_SETUP_COMPLETE);
519 }
520 }
521 }
522 }
523
handleGnssAsyncResult(const chreAsyncResult * result)524 void Manager::handleGnssAsyncResult(const chreAsyncResult *result) {
525 bool success = false;
526 switch (result->requestType) {
527 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START: {
528 if (mTestSession->feature != Feature::GNSS_LOCATION) {
529 LOGE("Unexpected GNSS location async result: test feature %" PRIu8,
530 static_cast<uint8_t>(mTestSession->feature));
531 } else {
532 success = validateAsyncResult(
533 result, static_cast<const void *>(&kGnssLocationCookie));
534 chreGnssLocationSessionStopAsync(&kGnssLocationCookie);
535 }
536 break;
537 }
538 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START: {
539 if (mTestSession->feature != Feature::GNSS_MEASUREMENT) {
540 LOGE("Unexpected GNSS measurement async result: test feature %" PRIu8,
541 static_cast<uint8_t>(mTestSession->feature));
542 } else {
543 success = validateAsyncResult(
544 result, static_cast<const void *>(&kGnssMeasurementCookie));
545 chreGnssMeasurementSessionStopAsync(&kGnssMeasurementCookie);
546 }
547 break;
548 }
549 default:
550 LOGE("Unexpected GNSS request type %" PRIu8, result->requestType);
551 }
552
553 sendTestResult(mTestSession->hostEndpointId, success);
554 }
555
handleWwanCellInfoResult(const chreWwanCellInfoResult * result)556 void Manager::handleWwanCellInfoResult(const chreWwanCellInfoResult *result) {
557 bool success = false;
558 // For WWAN, we treat "DISABLED" as success but with empty results, per
559 // CHRE API requirements.
560 if (mTestSession->feature != Feature::WWAN_CELL_INFO) {
561 LOGE("Unexpected WWAN cell info result: test feature %" PRIu8,
562 static_cast<uint8_t>(mTestSession->feature));
563 } else if (result->cookie != &kWwanCellInfoCookie) {
564 LOGE("Unexpected cookie on WWAN cell info result");
565 } else if (result->errorCode != CHRE_ERROR_NONE) {
566 LOGE("WWAN cell info result failed: error code %" PRIu8, result->errorCode);
567 } else if (mTestSession->featureState == FeatureState::DISABLED &&
568 result->cellInfoCount > 0) {
569 // Allow some retries to wait for the modem to clear the cell info cache.
570 if (mWwanRequestRetries >= kMaxWwanRequestRetries) {
571 LOGE(
572 "WWAN cell info result should be empty when disabled. Hit retry "
573 "limit (%" PRIu8 "), cell_info_count= %" PRIu8,
574 kMaxWwanRequestRetries, result->cellInfoCount);
575 } else {
576 mWwanRequestRetries++;
577 uint64_t delay = kOneSecondInNanoseconds * 1;
578 gWwanRequestRetryTimerHandle =
579 chreTimerSet(delay, &kWwanRequestRetryTimerCookie, /*oneShot=*/true);
580 if (gWwanRequestRetryTimerHandle != CHRE_TIMER_INVALID) {
581 LOGW("WWAN cell info result should be empty when disabled: count %" PRIu8
582 " Retrying after delay=%" PRIu64 "ns, num_retries=%" PRIu8
583 "/%" PRIu8,
584 result->cellInfoCount, delay, mWwanRequestRetries,
585 kMaxWwanRequestRetries);
586 return;
587 }
588 LOGE("Failed to set WWAN cell info retry timer");
589 }
590 } else {
591 success = true;
592 }
593
594 sendTestResult(mTestSession->hostEndpointId, success);
595 }
596
597 // The MicDisabled Settings test works as follows:
598 // * The contents of the Source Status Event are parsed, and there are 4
599 // possible scenarios for the flow of our test:
600 //
601 // - Mic Access was disabled, source was suspended
602 // -- Since CHRE guarantees that we'll receive audio data events spaced at
603 // the source's minBufferDuration apart (plus a small delay/latency),
604 // we set a timer for (minBufferDuration + 1) seconds to verify that no
605 // data event was received. We pass the test on a timeout.
606 //
607 // - Mic Access was disabled, source wasn't suspended
608 // -- We fail the test
609 //
610 // - Mic Access was enabled, source was suspended
611 // -- We fail the test
612 //
613 // - Mic Access was enabled, source wasn't suspended
614 // -- We set a flag 'GotSourceEnabledEvent'. The audio data event checks this
615 // flag, and reports success/failure appropriately.
616
handleAudioSourceStatusEvent(const struct chreAudioSourceStatusEvent * event)617 void Manager::handleAudioSourceStatusEvent(
618 const struct chreAudioSourceStatusEvent *event) {
619 LOGI("Received sampling status event suspended %d", event->status.suspended);
620 mAudioSamplingEnabled = !event->status.suspended;
621 if (!mTestSession.has_value()) {
622 return;
623 }
624
625 bool success = false;
626 if (mTestSession->featureState == FeatureState::ENABLED) {
627 if (event->status.suspended) {
628 if (gAudioStatusTimerHandle != CHRE_TIMER_INVALID) {
629 chreTimerCancel(gAudioStatusTimerHandle);
630 gAudioStatusTimerHandle = CHRE_TIMER_INVALID;
631 }
632
633 struct chreAudioSource source;
634 if (chreAudioGetSource(kAudioHandle, &source)) {
635 const uint64_t duration =
636 source.minBufferDuration + kOneSecondInNanoseconds;
637 gAudioDataTimerHandle =
638 chreTimerSet(duration, &kAudioDataTimerCookie, /*oneShot=*/true);
639
640 if (gAudioDataTimerHandle == CHRE_TIMER_INVALID) {
641 LOGE("Failed to set data check timer");
642 } else {
643 success = true;
644 }
645 } else {
646 LOGE("Failed to query audio source");
647 }
648 } else {
649 // There might be a corner case where CHRE might have queued an audio
650 // available event just as the microphone disable setting change is
651 // received that might wrongfully indicate that microphone access
652 // wasn't disabled when it is dispatched. We add a 2 second timer to
653 // allow CHRE to send the source status change event to account for
654 // this, and fail the test if the timer expires without getting said
655 // event.
656 LOGW("Source wasn't suspended when Mic Access disabled, waiting 2 sec");
657 gAudioStatusTimerHandle =
658 chreTimerSet(2 * kOneSecondInNanoseconds, &kAudioStatusTimerCookie,
659 /*oneShot=*/true);
660 if (gAudioStatusTimerHandle == CHRE_TIMER_INVALID) {
661 LOGE("Failed to set audio status check timer");
662 } else {
663 // continue the test, fail on timeout.
664 success = true;
665 }
666 }
667 } else {
668 gGotSourceEnabledEvent = true;
669 success = true;
670 }
671
672 if (!success) {
673 sendTestResult(mTestSession->hostEndpointId, success);
674 }
675 }
676
handleAudioDataEvent(const struct chreAudioDataEvent * event)677 void Manager::handleAudioDataEvent(const struct chreAudioDataEvent *event) {
678 UNUSED_VAR(event);
679
680 bool success = false;
681 if (mTestSession.has_value()) {
682 if (mTestSession->featureState == FeatureState::ENABLED) {
683 if (gAudioDataTimerHandle != CHRE_TIMER_INVALID) {
684 chreTimerCancel(gAudioDataTimerHandle);
685 gAudioDataTimerHandle = CHRE_TIMER_INVALID;
686 }
687 } else if (gGotSourceEnabledEvent) {
688 success = true;
689 }
690 chreAudioConfigureSource(kAudioHandle, /*enable=*/false,
691 /*minBufferDuration=*/0,
692 /*maxbufferDuration=*/0);
693 sendTestResult(mTestSession->hostEndpointId, success);
694 }
695 }
696
handleTimerEvent(const void * eventData)697 void Manager::handleTimerEvent(const void *eventData) {
698 bool testSuccess = false;
699 auto *cookie = static_cast<const uint32_t *>(eventData);
700
701 if (*cookie == kRangingRequestSetupRetryTimerCookie) {
702 gRangingRequestRetryTimerHandle = CHRE_TIMER_INVALID;
703 chreWifiRequestScanAsyncDefault(&kWifiScanningCookie);
704 return;
705 }
706
707 if (*cookie == kRangingRequestRetryTimerCookie) {
708 gRangingRequestRetryTimerHandle = CHRE_TIMER_INVALID;
709 requestRangingForFeatureWifiRtt();
710 return;
711 }
712
713 if (*cookie == kWwanRequestRetryTimerCookie) {
714 gWwanRequestRetryTimerHandle = CHRE_TIMER_INVALID;
715 if (chreWwanGetCellInfoAsync(&kWwanCellInfoCookie)) {
716 return;
717 }
718 LOGE("Failed to re-request WWAN cell info, rejected for processing");
719 }
720
721 // Ignore the audio status timer if the suspended status was received.
722 if (*cookie == kAudioStatusTimerCookie && !mAudioSamplingEnabled) {
723 gAudioStatusTimerHandle = CHRE_TIMER_INVALID;
724 return;
725 }
726
727 if (*cookie == kAudioDataTimerCookie) {
728 gAudioDataTimerHandle = CHRE_TIMER_INVALID;
729 testSuccess = true;
730 } else if (*cookie == kAudioStatusTimerCookie) {
731 gAudioStatusTimerHandle = CHRE_TIMER_INVALID;
732 LOGE("Source wasn't suspended when Mic Access was disabled");
733 } else {
734 LOGE("Invalid timer cookie: %" PRIx32, *cookie);
735 }
736 chreAudioConfigureSource(/*handle=*/0, /*enable=*/false,
737 /*minBufferDuration=*/0, /*maxBufferDuration=*/0);
738 sendTestResult(mTestSession->hostEndpointId, testSuccess);
739 }
740
handleBleAsyncResult(const chreAsyncResult * result)741 void Manager::handleBleAsyncResult(const chreAsyncResult *result) {
742 bool success = false;
743 switch (result->requestType) {
744 case CHRE_BLE_REQUEST_TYPE_START_SCAN: {
745 if (mTestSession->feature != Feature::BLE_SCANNING) {
746 LOGE("Unexpected BLE scan async result: test feature %" PRIu8,
747 static_cast<uint8_t>(mTestSession->feature));
748 } else {
749 success = validateAsyncResult(result, nullptr);
750 }
751 break;
752 }
753 default:
754 LOGE("Unexpected BLE request type %" PRIu8, result->requestType);
755 }
756
757 sendTestResult(mTestSession->hostEndpointId, success);
758 }
759
sendTestResult(uint16_t hostEndpointId,bool success)760 void Manager::sendTestResult(uint16_t hostEndpointId, bool success) {
761 test_shared::sendTestResultToHost(hostEndpointId,
762 chre_settings_test_MessageType_TEST_RESULT,
763 success, /*abortOnFailure=*/false);
764 mTestSession.reset();
765 mCachedRangingTarget.reset();
766 }
767
768 } // namespace settings_test
769
770 } // namespace chre
771