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