• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/nanoapp/callbacks.h"
23 #include "chre/util/nanoapp/log.h"
24 #include "chre/util/time.h"
25 #include "chre_settings_test.nanopb.h"
26 #include "chre_settings_test_util.h"
27 
28 #define LOG_TAG "[ChreSettingsTest]"
29 
30 namespace chre {
31 
32 namespace settings_test {
33 
34 namespace {
35 
36 constexpr uint32_t kWifiScanningCookie = 0x1234;
37 constexpr uint32_t kWifiRttCookie = 0x2345;
38 constexpr uint32_t kGnssLocationCookie = 0x3456;
39 constexpr uint32_t kGnssMeasurementCookie = 0x4567;
40 constexpr uint32_t kWwanCellInfoCookie = 0x5678;
41 
42 // Flag to verify if an audio data event was received after a valid sampling
43 // change event (i.e., we only got the data event after a source-enabled-and-
44 // not-suspended event).
45 bool gGotSourceEnabledEvent = false;
46 
47 uint32_t gTimerHandle = CHRE_TIMER_INVALID;
48 
getFeature(const chre_settings_test_TestCommand & command,Manager::Feature * feature)49 bool getFeature(const chre_settings_test_TestCommand &command,
50                 Manager::Feature *feature) {
51   bool success = true;
52   switch (command.feature) {
53     case chre_settings_test_TestCommand_Feature_WIFI_SCANNING:
54       *feature = Manager::Feature::WIFI_SCANNING;
55       break;
56     case chre_settings_test_TestCommand_Feature_WIFI_RTT:
57       *feature = Manager::Feature::WIFI_RTT;
58       break;
59     case chre_settings_test_TestCommand_Feature_GNSS_LOCATION:
60       *feature = Manager::Feature::GNSS_LOCATION;
61       break;
62     case chre_settings_test_TestCommand_Feature_GNSS_MEASUREMENT:
63       *feature = Manager::Feature::GNSS_MEASUREMENT;
64       break;
65     case chre_settings_test_TestCommand_Feature_WWAN_CELL_INFO:
66       *feature = Manager::Feature::WWAN_CELL_INFO;
67       break;
68     case chre_settings_test_TestCommand_Feature_AUDIO:
69       *feature = Manager::Feature::AUDIO;
70       break;
71     default:
72       LOGE("Unknown feature %d", command.feature);
73       success = false;
74   }
75 
76   return success;
77 }
78 
getFeatureState(const chre_settings_test_TestCommand & command,Manager::FeatureState * state)79 bool getFeatureState(const chre_settings_test_TestCommand &command,
80                      Manager::FeatureState *state) {
81   bool success = true;
82   switch (command.state) {
83     case chre_settings_test_TestCommand_State_ENABLED:
84       *state = Manager::FeatureState::ENABLED;
85       break;
86     case chre_settings_test_TestCommand_State_DISABLED:
87       *state = Manager::FeatureState::DISABLED;
88       break;
89     default:
90       LOGE("Unknown feature state %d", command.state);
91       success = false;
92   }
93 
94   return success;
95 }
96 
getTestStep(const chre_settings_test_TestCommand & command,Manager::TestStep * step)97 bool getTestStep(const chre_settings_test_TestCommand &command,
98                  Manager::TestStep *step) {
99   bool success = true;
100   switch (command.step) {
101     case chre_settings_test_TestCommand_Step_SETUP:
102       *step = Manager::TestStep::SETUP;
103       break;
104     case chre_settings_test_TestCommand_Step_START:
105       *step = Manager::TestStep::START;
106       break;
107     default:
108       LOGE("Unknown test step %d", command.step);
109       success = false;
110   }
111 
112   return success;
113 }
114 
isTestSupported()115 bool isTestSupported() {
116   // CHRE settings requirements were introduced in CHRE v1.4
117   return chreGetVersion() >= CHRE_API_VERSION_1_4;
118 }
119 
120 }  // anonymous namespace
121 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)122 void Manager::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
123                           const void *eventData) {
124   if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) {
125     handleMessageFromHost(
126         senderInstanceId,
127         static_cast<const chreMessageFromHostData *>(eventData));
128   } else if (senderInstanceId == CHRE_INSTANCE_ID) {
129     handleDataFromChre(eventType, eventData);
130   } else {
131     LOGW("Got unknown event type from senderInstanceId %" PRIu32
132          " and with eventType %" PRIu16,
133          senderInstanceId, eventType);
134   }
135 }
136 
isFeatureSupported(Feature feature)137 bool Manager::isFeatureSupported(Feature feature) {
138   bool supported = false;
139 
140   uint32_t version = chreGetVersion();
141   switch (feature) {
142     case Feature::WIFI_SCANNING: {
143       uint32_t capabilities = chreWifiGetCapabilities();
144       supported = (version >= CHRE_API_VERSION_1_1) &&
145                   ((capabilities & CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN) != 0);
146       break;
147     }
148     case Feature::WIFI_RTT: {
149       uint32_t capabilities = chreWifiGetCapabilities();
150       supported = (version >= CHRE_API_VERSION_1_2) &&
151                   ((capabilities & CHRE_WIFI_CAPABILITIES_RTT_RANGING) != 0);
152       break;
153     }
154     case Feature::GNSS_LOCATION: {
155       uint32_t capabilities = chreGnssGetCapabilities();
156       supported = (version >= CHRE_API_VERSION_1_1) &&
157                   ((capabilities & CHRE_GNSS_CAPABILITIES_LOCATION) != 0);
158       break;
159     }
160     case Feature::GNSS_MEASUREMENT: {
161       uint32_t capabilities = chreGnssGetCapabilities();
162       supported = (version >= CHRE_API_VERSION_1_1) &&
163                   ((capabilities & CHRE_GNSS_CAPABILITIES_MEASUREMENTS) != 0);
164       break;
165     }
166     case Feature::WWAN_CELL_INFO: {
167       uint32_t capabilities = chreWwanGetCapabilities();
168       supported = (version >= CHRE_API_VERSION_1_1) &&
169                   ((capabilities & CHRE_WWAN_GET_CELL_INFO) != 0);
170       break;
171     }
172     case Feature::AUDIO: {
173       struct chreAudioSource source;
174       supported = chreAudioGetSource(0 /* handle */, &source);
175       break;
176     }
177     default:
178       LOGE("Unknown feature %" PRIu8, static_cast<uint8_t>(feature));
179   }
180 
181   return supported;
182 }
183 
handleMessageFromHost(uint32_t senderInstanceId,const chreMessageFromHostData * hostData)184 void Manager::handleMessageFromHost(uint32_t senderInstanceId,
185                                     const chreMessageFromHostData *hostData) {
186   bool success = false;
187   uint32_t messageType = hostData->messageType;
188   if (senderInstanceId != CHRE_INSTANCE_ID) {
189     LOGE("Incorrect sender instance id: %" PRIu32, senderInstanceId);
190   } else if (messageType != chre_settings_test_MessageType_TEST_COMMAND) {
191     LOGE("Invalid message type %" PRIu32, messageType);
192   } else {
193     pb_istream_t istream = pb_istream_from_buffer(
194         static_cast<const pb_byte_t *>(hostData->message),
195         hostData->messageSize);
196     chre_settings_test_TestCommand testCommand =
197         chre_settings_test_TestCommand_init_default;
198 
199     if (!pb_decode(&istream, chre_settings_test_TestCommand_fields,
200                    &testCommand)) {
201       LOGE("Failed to decode start command error %s", PB_GET_ERROR(&istream));
202     } else {
203       Feature feature;
204       FeatureState state;
205       TestStep step;
206       if (getFeature(testCommand, &feature) &&
207           getFeatureState(testCommand, &state) &&
208           getTestStep(testCommand, &step)) {
209         LOGD("starting test: feature: %u, state %u, step %u",
210              static_cast<uint8_t>(feature), static_cast<uint8_t>(state),
211              static_cast<uint8_t>(step));
212         handleStartTestMessage(hostData->hostEndpoint, feature, state, step);
213         success = true;
214       }
215     }
216   }
217 
218   if (!success) {
219     sendTestResultToHost(hostData->hostEndpoint, false /* success */);
220   }
221 }
222 
handleStartTestMessage(uint16_t hostEndpointId,Feature feature,FeatureState state,TestStep step)223 void Manager::handleStartTestMessage(uint16_t hostEndpointId, Feature feature,
224                                      FeatureState state, TestStep step) {
225   // If the test/feature is not supported, treat as success and skip the test.
226   if (!isTestSupported() || !isFeatureSupported(feature)) {
227     LOGW("Skipping test - TestSupported: %u, FeatureSupported: %u",
228          isTestSupported(), isFeatureSupported(feature));
229     sendTestResult(hostEndpointId, true /* success */);
230   } else {
231     bool success = false;
232     if (step == TestStep::SETUP) {
233       if (feature != Feature::WIFI_RTT) {
234         LOGE("Unexpected feature %" PRIu8 " for test step",
235              static_cast<uint8_t>(feature));
236       } else {
237         success = chreWifiRequestScanAsyncDefault(&kWifiScanningCookie);
238       }
239     } else {
240       success = startTestForFeature(feature);
241     }
242 
243     if (!success) {
244       sendTestResult(hostEndpointId, false /* success */);
245     } else {
246       mTestSession = TestSession(hostEndpointId, feature, state, step);
247     }
248   }
249 }
250 
handleDataFromChre(uint16_t eventType,const void * eventData)251 void Manager::handleDataFromChre(uint16_t eventType, const void *eventData) {
252   if (mTestSession.has_value()) {
253     // The validation for the correct data w.r.t. the current test session
254     // will be done in the methods called from here.
255     switch (eventType) {
256       case CHRE_EVENT_AUDIO_DATA:
257         handleAudioDataEvent(
258             static_cast<const struct chreAudioDataEvent *>(eventData));
259         break;
260 
261       case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
262         handleAudioSourceStatusEvent(
263             static_cast<const struct chreAudioSourceStatusEvent *>(eventData));
264         break;
265 
266       case CHRE_EVENT_TIMER:
267         handleTimeout();
268         break;
269 
270       case CHRE_EVENT_WIFI_ASYNC_RESULT:
271         handleWifiAsyncResult(static_cast<const chreAsyncResult *>(eventData));
272         break;
273 
274       case CHRE_EVENT_WIFI_SCAN_RESULT:
275         handleWifiScanResult(static_cast<const chreWifiScanEvent *>(eventData));
276         break;
277 
278       case CHRE_EVENT_GNSS_ASYNC_RESULT:
279         handleGnssAsyncResult(static_cast<const chreAsyncResult *>(eventData));
280         break;
281 
282       case CHRE_EVENT_WWAN_CELL_INFO_RESULT:
283         handleWwanCellInfoResult(
284             static_cast<const chreWwanCellInfoResult *>(eventData));
285         break;
286 
287       default:
288         LOGE("Unknown event type %" PRIu16, eventType);
289     }
290   }
291 }
292 
startTestForFeature(Feature feature)293 bool Manager::startTestForFeature(Feature feature) {
294   bool success = true;
295   switch (feature) {
296     case Feature::WIFI_SCANNING:
297       success = chreWifiRequestScanAsyncDefault(&kWifiScanningCookie);
298       break;
299 
300     case Feature::WIFI_RTT: {
301       if (!mCachedRangingTarget.has_value()) {
302         LOGE("No cached WiFi RTT ranging target");
303       } else {
304         struct chreWifiRangingParams params = {
305             .targetListLen = 1, .targetList = &mCachedRangingTarget.value()};
306         success = chreWifiRequestRangingAsync(&params, &kWifiRttCookie);
307       }
308       break;
309     }
310 
311     case Feature::GNSS_LOCATION:
312       success = chreGnssLocationSessionStartAsync(1000 /* minIntervalMs */,
313                                                   0 /* minTimeToNextFixMs */,
314                                                   &kGnssLocationCookie);
315       break;
316 
317     case Feature::GNSS_MEASUREMENT:
318       success = chreGnssMeasurementSessionStartAsync(1000 /* minIntervalMs */,
319                                                      &kGnssMeasurementCookie);
320       break;
321 
322     case Feature::WWAN_CELL_INFO:
323       success = chreWwanGetCellInfoAsync(&kWwanCellInfoCookie);
324       break;
325 
326     case Feature::AUDIO: {
327       struct chreAudioSource source;
328       if ((success = chreAudioGetSource(0 /* handle */, &source))) {
329         success = chreAudioConfigureSource(0 /* handle */, true /* enable */,
330                                            source.minBufferDuration,
331                                            source.minBufferDuration);
332       }
333       break;
334     }
335 
336     default:
337       LOGE("Unknown feature %" PRIu8, static_cast<uint8_t>(feature));
338       return false;
339   }
340 
341   if (!success) {
342     LOGE("Failed to make request for test feature %" PRIu8,
343          static_cast<uint8_t>(feature));
344   } else {
345     LOGI("Starting test for feature %" PRIu8, static_cast<uint8_t>(feature));
346   }
347 
348   return success;
349 }
350 
validateAsyncResult(const chreAsyncResult * result,const void * expectedCookie)351 bool Manager::validateAsyncResult(const chreAsyncResult *result,
352                                   const void *expectedCookie) {
353   bool success = false;
354   if (result->cookie != expectedCookie) {
355     LOGE("Unexpected cookie on async result");
356   } else {
357     bool featureEnabled = (mTestSession->featureState == FeatureState::ENABLED);
358     bool disabledErrorCode =
359         (result->errorCode == CHRE_ERROR_FUNCTION_DISABLED);
360 
361     if (featureEnabled && disabledErrorCode) {
362       LOGE("Got disabled error code when feature is enabled");
363     } else if (!featureEnabled && !disabledErrorCode) {
364       LOGE("Got non-disabled error code when feature is disabled");
365     } else {
366       success = true;
367     }
368   }
369 
370   return success;
371 }
372 
handleWifiAsyncResult(const chreAsyncResult * result)373 void Manager::handleWifiAsyncResult(const chreAsyncResult *result) {
374   bool success = false;
375   switch (result->requestType) {
376     case CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN: {
377       if (mTestSession->feature == Feature::WIFI_RTT) {
378         // Ignore validating the scan async response since we only care about
379         // the actual scan event to initiate the RTT request. A failure to
380         // receive the scan response should cause a timeout at the host.
381         return;
382       }
383       if (mTestSession->feature != Feature::WIFI_SCANNING) {
384         LOGE("Unexpected WiFi scan async result: test feature %" PRIu8,
385              static_cast<uint8_t>(mTestSession->feature));
386       } else {
387         success = validateAsyncResult(
388             result, static_cast<const void *>(&kWifiScanningCookie));
389       }
390       break;
391     }
392     case CHRE_WIFI_REQUEST_TYPE_RANGING: {
393       if (mTestSession->feature != Feature::WIFI_RTT) {
394         LOGE("Unexpected WiFi ranging async result: test feature %" PRIu8,
395              static_cast<uint8_t>(mTestSession->feature));
396       } else {
397         success = validateAsyncResult(
398             result, static_cast<const void *>(&kWifiRttCookie));
399       }
400       break;
401     }
402     default:
403       LOGE("Unexpected WiFi request type %" PRIu8, result->requestType);
404   }
405 
406   sendTestResult(mTestSession->hostEndpointId, success);
407 }
408 
handleWifiScanResult(const chreWifiScanEvent * result)409 void Manager::handleWifiScanResult(const chreWifiScanEvent *result) {
410   if (mTestSession->feature == Feature::WIFI_RTT &&
411       mTestSession->step == TestStep::SETUP) {
412     if (result->resultCount == 0) {
413       LOGE("Received empty WiFi scan result");
414       sendTestResult(mTestSession->hostEndpointId, false /* success */);
415     } else {
416       chreWifiRangingTarget target;
417       // Try to find an AP with the FTM responder flag set. The RTT ranging
418       // request should still work equivalently even if the flag is not set (but
419       // possibly with an error in the ranging result), so we use the last entry
420       // if none is found.
421       size_t index = result->resultCount - 1;
422       for (uint8_t i = 0; i < result->resultCount - 1; i++) {
423         if ((result->results[i].flags &
424              CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER) != 0) {
425           index = i;
426           break;
427         }
428       }
429       chreWifiRangingTargetFromScanResult(&result->results[index], &target);
430       mCachedRangingTarget = target;
431 
432       sendEmptyMessageToHost(
433           mTestSession->hostEndpointId,
434           chre_settings_test_MessageType_TEST_SETUP_COMPLETE);
435     }
436   }
437 }
438 
handleGnssAsyncResult(const chreAsyncResult * result)439 void Manager::handleGnssAsyncResult(const chreAsyncResult *result) {
440   bool success = false;
441   switch (result->requestType) {
442     case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START: {
443       if (mTestSession->feature != Feature::GNSS_LOCATION) {
444         LOGE("Unexpected GNSS location async result: test feature %" PRIu8,
445              static_cast<uint8_t>(mTestSession->feature));
446       } else {
447         success = validateAsyncResult(
448             result, static_cast<const void *>(&kGnssLocationCookie));
449         chreGnssLocationSessionStopAsync(&kGnssLocationCookie);
450       }
451       break;
452     }
453     case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START: {
454       if (mTestSession->feature != Feature::GNSS_MEASUREMENT) {
455         LOGE("Unexpected GNSS measurement async result: test feature %" PRIu8,
456              static_cast<uint8_t>(mTestSession->feature));
457       } else {
458         success = validateAsyncResult(
459             result, static_cast<const void *>(&kGnssMeasurementCookie));
460         chreGnssMeasurementSessionStopAsync(&kGnssMeasurementCookie);
461       }
462       break;
463     }
464     default:
465       LOGE("Unexpected GNSS request type %" PRIu8, result->requestType);
466   }
467 
468   sendTestResult(mTestSession->hostEndpointId, success);
469 }
470 
handleWwanCellInfoResult(const chreWwanCellInfoResult * result)471 void Manager::handleWwanCellInfoResult(const chreWwanCellInfoResult *result) {
472   bool success = false;
473   // For WWAN, we treat "DISABLED" as success but with empty results, per
474   // CHRE API requirements.
475   if (mTestSession->feature != Feature::WWAN_CELL_INFO) {
476     LOGE("Unexpected WWAN cell info result: test feature %" PRIu8,
477          static_cast<uint8_t>(mTestSession->feature));
478   } else if (result->cookie != &kWwanCellInfoCookie) {
479     LOGE("Unexpected cookie on WWAN cell info result");
480   } else if (result->errorCode != CHRE_ERROR_NONE) {
481     LOGE("WWAN cell info result failed: error code %" PRIu8, result->errorCode);
482   } else if (mTestSession->featureState == FeatureState::DISABLED &&
483              result->cellInfoCount > 0) {
484     LOGE("WWAN cell info result should be empty when disabled: count %" PRIu8,
485          result->cellInfoCount);
486   } else {
487     success = true;
488   }
489 
490   sendTestResult(mTestSession->hostEndpointId, success);
491 }
492 
493 // The MicDisabled Settings test works as follows:
494 // * The contents of the Source Status Event are parsed, and there are 4
495 //   possible scenarios for the flow of our test:
496 //
497 // - Mic Access was disabled, source was suspended
498 // -- Since CHRE guarantees that we'll receive audio data events spaced at
499 //    the source's minBufferDuration apart (plus a small delay/latency),
500 //    we set a timer for (minBufferDuration + 1) seconds to verify that no
501 //    data event was received. We pass the test on a timeout.
502 //
503 // - Mic Access was disabled, source wasn't suspended
504 // -- We fail the test
505 //
506 // - Mic Access was enabled, source was suspended
507 // -- We fail the test
508 //
509 // - Mic Access was enabled, source wasn't suspended
510 // -- We set a flag 'GotSourceEnabledEvent'. The audio data event checks this
511 // flag, and reports success/failure appropriately.
512 
handleAudioSourceStatusEvent(const struct chreAudioSourceStatusEvent * event)513 void Manager::handleAudioSourceStatusEvent(
514     const struct chreAudioSourceStatusEvent *event) {
515   bool success = false;
516   if (mTestSession.has_value()) {
517     if (mTestSession->featureState == FeatureState::ENABLED) {
518       if (event->status.suspended) {
519         struct chreAudioSource source;
520         if (chreAudioGetSource(0 /* handle */, &source)) {
521           const uint64_t duration =
522               source.minBufferDuration + kOneSecondInNanoseconds;
523           gTimerHandle =
524               chreTimerSet(duration, nullptr /* cookie */, true /* oneShot */);
525 
526           if (gTimerHandle == CHRE_TIMER_INVALID) {
527             LOGE("Failed to set timer");
528           } else {
529             success = true;
530           }
531         } else {
532           LOGE("Failed to query audio source");
533         }
534       } else {
535         LOGE("Source wasn't suspended when Mic Access was disabled");
536       }
537     } else {
538       gGotSourceEnabledEvent = true;
539       success = true;
540     }
541 
542     if (!success) {
543       sendTestResult(mTestSession->hostEndpointId, success);
544     }
545   }
546 }
547 
handleAudioDataEvent(const struct chreAudioDataEvent * event)548 void Manager::handleAudioDataEvent(const struct chreAudioDataEvent *event) {
549   bool success = false;
550   if (mTestSession.has_value()) {
551     if (mTestSession->featureState == FeatureState::ENABLED) {
552       if (gTimerHandle != CHRE_TIMER_INVALID) {
553         chreTimerCancel(gTimerHandle);
554         gTimerHandle = CHRE_TIMER_INVALID;
555       }
556     } else if (gGotSourceEnabledEvent) {
557       success = true;
558     }
559     chreAudioConfigureSource(0 /* handle */, false /* enable */,
560                              0 /* minBufferDuration */,
561                              0 /* maxbufferDuration */);
562     sendTestResult(mTestSession->hostEndpointId, success);
563   }
564 }
565 
handleTimeout()566 void Manager::handleTimeout() {
567   gTimerHandle = CHRE_TIMER_INVALID;
568   chreAudioConfigureSource(0 /*handle*/, false /*enable*/,
569                            0 /*minBufferDuration*/, 0 /*maxBufferDuration*/);
570   sendTestResult(mTestSession->hostEndpointId, true /*success*/);
571 }
572 
sendTestResult(uint16_t hostEndpointId,bool success)573 void Manager::sendTestResult(uint16_t hostEndpointId, bool success) {
574   sendTestResultToHost(hostEndpointId, success);
575   mTestSession.reset();
576   mCachedRangingTarget.reset();
577 }
578 
579 }  // namespace settings_test
580 
581 }  // namespace chre
582