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 "chre_api/chre/gnss.h"
18
19 #include <cstdint>
20 #include <functional>
21
22 #include "chre/core/event_loop_manager.h"
23 #include "chre/core/settings.h"
24 #include "chre/platform/linux/pal_gnss.h"
25 #include "chre/platform/log.h"
26 #include "chre/util/system/napp_permissions.h"
27 #include "chre_api/chre/event.h"
28 #include "gtest/gtest.h"
29 #include "inc/test_util.h"
30 #include "test_base.h"
31 #include "test_event.h"
32 #include "test_event_queue.h"
33 #include "test_util.h"
34
35 namespace chre {
36 namespace {
37
38 class GnssTest : public TestBase {};
39
40 /**
41 * Wait for the predicate to become true with a timeout.
42 *
43 * @return the last value of the predicate.
44 */
waitForCondition(const std::function<bool ()> & predicate,std::chrono::milliseconds timeout)45 bool waitForCondition(const std::function<bool()> &predicate,
46 std::chrono::milliseconds timeout) {
47 constexpr std::chrono::milliseconds kSleepDuration(100);
48 bool result;
49 std::chrono::milliseconds time;
50 while (!(result = predicate()) && time < timeout) {
51 std::this_thread::sleep_for(kSleepDuration);
52 time += kSleepDuration;
53 }
54 return result;
55 }
56
57 // ref b/228669574
TEST_F(GnssTest,GnssSubscriptionWithSettingChange)58 TEST_F(GnssTest, GnssSubscriptionWithSettingChange) {
59 CREATE_CHRE_TEST_EVENT(LOCATION_REQUEST, 0);
60
61 struct LocationRequest {
62 bool enable;
63 uint32_t cookie;
64 };
65
66 class App : public TestNanoapp {
67 public:
68 App()
69 : TestNanoapp(
70 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
71
72 bool start() override {
73 chreUserSettingConfigureEvents(CHRE_USER_SETTING_LOCATION,
74 true /*enabled*/);
75 return true;
76 }
77
78 void handleEvent(uint32_t, uint16_t eventType,
79 const void *eventData) override {
80 switch (eventType) {
81 case CHRE_EVENT_GNSS_ASYNC_RESULT: {
82 auto *event = static_cast<const chreAsyncResult *>(eventData);
83 if (event->success) {
84 TestEventQueueSingleton::get()->pushEvent(
85 CHRE_EVENT_GNSS_ASYNC_RESULT,
86 *(static_cast<const uint32_t *>(event->cookie)));
87 }
88 break;
89 }
90
91 case CHRE_EVENT_SETTING_CHANGED_LOCATION: {
92 TestEventQueueSingleton::get()->pushEvent(
93 CHRE_EVENT_SETTING_CHANGED_LOCATION);
94 break;
95 }
96
97 case CHRE_EVENT_TEST_EVENT: {
98 auto event = static_cast<const TestEvent *>(eventData);
99 switch (event->type) {
100 case LOCATION_REQUEST: {
101 auto request = static_cast<const LocationRequest *>(event->data);
102 bool success;
103 mCookie = request->cookie;
104 if (request->enable) {
105 success = chreGnssLocationSessionStartAsync(
106 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/,
107 &mCookie);
108 } else {
109 success = chreGnssLocationSessionStopAsync(&mCookie);
110 }
111 TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST,
112 success);
113 break;
114 }
115 }
116 }
117 }
118 }
119
120 void end() override {
121 chreUserSettingConfigureEvents(CHRE_USER_SETTING_LOCATION,
122 false /*enabled*/);
123 }
124
125 protected:
126 uint32_t mCookie;
127 };
128
129 uint64_t appId = loadNanoapp(MakeUnique<App>());
130
131 bool success;
132 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
133 chrePalGnssDelaySendingLocationEvents(true);
134
135 LocationRequest request{.enable = true, .cookie = 0x123};
136 sendEventToNanoapp(appId, LOCATION_REQUEST, request);
137 waitForEvent(LOCATION_REQUEST, &success);
138 EXPECT_TRUE(success);
139 chrePalGnssStartSendingLocationEvents();
140 uint32_t cookie;
141 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
142 EXPECT_EQ(cookie, request.cookie);
143 EXPECT_TRUE(chrePalGnssIsLocationEnabled());
144
145 EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
146 Setting::LOCATION, false /* enabled */);
147
148 waitForEvent(CHRE_EVENT_SETTING_CHANGED_LOCATION);
149
150 // Wait for the setting change to propagate to GNSS.
151 EXPECT_TRUE(waitForCondition([]() { return !chrePalGnssIsLocationEnabled(); },
152 std::chrono::milliseconds(1000)));
153
154 EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
155 Setting::LOCATION, true /* enabled */);
156
157 waitForEvent(CHRE_EVENT_SETTING_CHANGED_LOCATION);
158
159 // Wait for the setting change to propagate to GNSS.
160 EXPECT_TRUE(waitForCondition([]() { return chrePalGnssIsLocationEnabled(); },
161 std::chrono::milliseconds(1000)));
162
163 request.enable = false;
164 sendEventToNanoapp(appId, LOCATION_REQUEST, request);
165 waitForEvent(LOCATION_REQUEST, &success);
166 EXPECT_TRUE(success);
167 chrePalGnssStartSendingLocationEvents();
168 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
169 EXPECT_EQ(cookie, request.cookie);
170 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
171 chrePalGnssDelaySendingLocationEvents(false);
172 }
173
TEST_F(GnssTest,GnssCanSubscribeAndUnsubscribeToLocation)174 TEST_F(GnssTest, GnssCanSubscribeAndUnsubscribeToLocation) {
175 CREATE_CHRE_TEST_EVENT(LOCATION_REQUEST, 0);
176
177 struct LocationRequest {
178 bool enable;
179 uint32_t cookie;
180 };
181
182 class App : public TestNanoapp {
183 public:
184 App()
185 : TestNanoapp(
186 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
187
188 void handleEvent(uint32_t, uint16_t eventType,
189 const void *eventData) override {
190 switch (eventType) {
191 case CHRE_EVENT_GNSS_ASYNC_RESULT: {
192 auto *event = static_cast<const chreAsyncResult *>(eventData);
193 if (event->success) {
194 TestEventQueueSingleton::get()->pushEvent(
195 CHRE_EVENT_GNSS_ASYNC_RESULT,
196 *(static_cast<const uint32_t *>(event->cookie)));
197 }
198 break;
199 }
200
201 case CHRE_EVENT_TEST_EVENT: {
202 auto event = static_cast<const TestEvent *>(eventData);
203 switch (event->type) {
204 case LOCATION_REQUEST: {
205 auto request = static_cast<const LocationRequest *>(event->data);
206 bool success;
207 mCookie = request->cookie;
208 if (request->enable) {
209 success = chreGnssLocationSessionStartAsync(
210 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/,
211 &mCookie);
212 } else {
213 success = chreGnssLocationSessionStopAsync(&mCookie);
214 }
215 TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST,
216 success);
217 break;
218 }
219 }
220 }
221 }
222 }
223
224 protected:
225 uint32_t mCookie;
226 };
227
228 uint64_t appId = loadNanoapp(MakeUnique<App>());
229
230 bool success;
231 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
232
233 LocationRequest request{.enable = true, .cookie = 0x123};
234 sendEventToNanoapp(appId, LOCATION_REQUEST, request);
235 waitForEvent(LOCATION_REQUEST, &success);
236 EXPECT_TRUE(success);
237 uint32_t cookie;
238 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
239 EXPECT_EQ(cookie, request.cookie);
240 EXPECT_TRUE(chrePalGnssIsLocationEnabled());
241
242 request.enable = false;
243 sendEventToNanoapp(appId, LOCATION_REQUEST, request);
244 waitForEvent(LOCATION_REQUEST, &success);
245 EXPECT_TRUE(success);
246 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
247 EXPECT_EQ(cookie, request.cookie);
248 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
249 }
250
TEST_F(GnssTest,GnssUnsubscribeToLocationOnUnload)251 TEST_F(GnssTest, GnssUnsubscribeToLocationOnUnload) {
252 CREATE_CHRE_TEST_EVENT(LOCATION_REQUEST, 0);
253
254 struct LocationRequest {
255 bool enable;
256 uint32_t cookie;
257 };
258
259 class App : public TestNanoapp {
260 public:
261 App()
262 : TestNanoapp(
263 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
264
265 void handleEvent(uint32_t, uint16_t eventType,
266 const void *eventData) override {
267 switch (eventType) {
268 case CHRE_EVENT_GNSS_ASYNC_RESULT: {
269 auto *event = static_cast<const chreAsyncResult *>(eventData);
270 if (event->success) {
271 TestEventQueueSingleton::get()->pushEvent(
272 CHRE_EVENT_GNSS_ASYNC_RESULT,
273 *(static_cast<const uint32_t *>(event->cookie)));
274 }
275 break;
276 }
277
278 case CHRE_EVENT_TEST_EVENT: {
279 auto event = static_cast<const TestEvent *>(eventData);
280 switch (event->type) {
281 case LOCATION_REQUEST: {
282 auto request = static_cast<const LocationRequest *>(event->data);
283 if (request->enable) {
284 mCookie = request->cookie;
285 const bool success = chreGnssLocationSessionStartAsync(
286 1000 /*minIntervalMs*/, 1000 /*minTimeToNextFixMs*/,
287 &mCookie);
288 TestEventQueueSingleton::get()->pushEvent(LOCATION_REQUEST,
289 success);
290 }
291 break;
292 }
293 }
294 }
295 }
296 }
297
298 protected:
299 uint32_t mCookie;
300 };
301
302 uint64_t appId = loadNanoapp(MakeUnique<App>());
303
304 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
305
306 LocationRequest request{.enable = true, .cookie = 0x123};
307 sendEventToNanoapp(appId, LOCATION_REQUEST, request);
308 bool success;
309 waitForEvent(LOCATION_REQUEST, &success);
310 EXPECT_TRUE(success);
311 uint32_t cookie;
312 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
313 EXPECT_EQ(cookie, request.cookie);
314 EXPECT_TRUE(chrePalGnssIsLocationEnabled());
315
316 unloadNanoapp(appId);
317 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
318 }
319
TEST_F(GnssTest,GnssCanSubscribeAndUnsubscribeToMeasurement)320 TEST_F(GnssTest, GnssCanSubscribeAndUnsubscribeToMeasurement) {
321 CREATE_CHRE_TEST_EVENT(MEASUREMENT_REQUEST, 0);
322
323 struct MeasurementRequest {
324 bool enable;
325 uint32_t cookie;
326 };
327
328 class App : public TestNanoapp {
329 public:
330 App()
331 : TestNanoapp(
332 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
333
334 void handleEvent(uint32_t, uint16_t eventType,
335 const void *eventData) override {
336 static uint32_t cookie;
337 switch (eventType) {
338 case CHRE_EVENT_GNSS_ASYNC_RESULT: {
339 auto *event = static_cast<const chreAsyncResult *>(eventData);
340 if (event->success) {
341 TestEventQueueSingleton::get()->pushEvent(
342 CHRE_EVENT_GNSS_ASYNC_RESULT,
343 *(static_cast<const uint32_t *>(event->cookie)));
344 }
345 break;
346 }
347
348 case CHRE_EVENT_TEST_EVENT: {
349 auto event = static_cast<const TestEvent *>(eventData);
350 switch (event->type) {
351 case MEASUREMENT_REQUEST: {
352 auto request =
353 static_cast<const MeasurementRequest *>(event->data);
354 bool success;
355 mCookie = request->cookie;
356 if (request->enable) {
357 success = chreGnssMeasurementSessionStartAsync(
358 1000 /*minIntervalMs*/, &mCookie);
359 } else {
360 cookie = request->cookie;
361 success = chreGnssMeasurementSessionStopAsync(&mCookie);
362 }
363 TestEventQueueSingleton::get()->pushEvent(MEASUREMENT_REQUEST,
364 success);
365 break;
366 }
367 }
368 }
369 }
370 }
371
372 protected:
373 uint32_t mCookie;
374 };
375
376 uint64_t appId = loadNanoapp(MakeUnique<App>());
377
378 bool success;
379 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
380
381 MeasurementRequest request{.enable = true, .cookie = 0x123};
382 sendEventToNanoapp(appId, MEASUREMENT_REQUEST, request);
383 waitForEvent(MEASUREMENT_REQUEST, &success);
384 EXPECT_TRUE(success);
385 uint32_t cookie;
386 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
387 EXPECT_EQ(cookie, request.cookie);
388 EXPECT_TRUE(chrePalGnssIsMeasurementEnabled());
389
390 request.enable = false;
391 sendEventToNanoapp(appId, MEASUREMENT_REQUEST, request);
392 waitForEvent(MEASUREMENT_REQUEST, &success);
393 EXPECT_TRUE(success);
394 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
395 EXPECT_EQ(cookie, request.cookie);
396 EXPECT_FALSE(chrePalGnssIsMeasurementEnabled());
397 }
398
TEST_F(GnssTest,GnssUnsubscribeToMeasurementOnUnload)399 TEST_F(GnssTest, GnssUnsubscribeToMeasurementOnUnload) {
400 CREATE_CHRE_TEST_EVENT(MEASUREMENT_REQUEST, 0);
401
402 struct MeasurementRequest {
403 bool enable;
404 uint32_t cookie;
405 };
406
407 class App : public TestNanoapp {
408 public:
409 App()
410 : TestNanoapp(
411 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
412
413 void handleEvent(uint32_t, uint16_t eventType,
414 const void *eventData) override {
415 switch (eventType) {
416 case CHRE_EVENT_GNSS_ASYNC_RESULT: {
417 auto *event = static_cast<const chreAsyncResult *>(eventData);
418 if (event->success) {
419 TestEventQueueSingleton::get()->pushEvent(
420 CHRE_EVENT_GNSS_ASYNC_RESULT,
421 *(static_cast<const uint32_t *>(event->cookie)));
422 }
423 break;
424 }
425
426 case CHRE_EVENT_TEST_EVENT: {
427 auto event = static_cast<const TestEvent *>(eventData);
428 switch (event->type) {
429 case MEASUREMENT_REQUEST: {
430 auto request =
431 static_cast<const MeasurementRequest *>(event->data);
432 if (request->enable) {
433 mCookie = request->cookie;
434 const bool success = chreGnssMeasurementSessionStartAsync(
435 1000 /*minIntervalMs*/, &mCookie);
436 TestEventQueueSingleton::get()->pushEvent(MEASUREMENT_REQUEST,
437 success);
438 }
439 break;
440 }
441 }
442 }
443 }
444 }
445
446 protected:
447 uint32_t mCookie;
448 };
449
450 uint64_t appId = loadNanoapp(MakeUnique<App>());
451
452 EXPECT_FALSE(chrePalGnssIsLocationEnabled());
453
454 MeasurementRequest request{.enable = true, .cookie = 0x123};
455 sendEventToNanoapp(appId, MEASUREMENT_REQUEST, request);
456 bool success;
457 waitForEvent(MEASUREMENT_REQUEST, &success);
458 EXPECT_TRUE(success);
459 uint32_t cookie;
460 waitForEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, &cookie);
461 EXPECT_EQ(cookie, request.cookie);
462 EXPECT_TRUE(chrePalGnssIsMeasurementEnabled());
463
464 unloadNanoapp(appId);
465 EXPECT_FALSE(chrePalGnssIsMeasurementEnabled());
466 }
467
TEST_F(GnssTest,GnssCanSubscribeAndUnsubscribeToPassiveListener)468 TEST_F(GnssTest, GnssCanSubscribeAndUnsubscribeToPassiveListener) {
469 CREATE_CHRE_TEST_EVENT(LISTENER_REQUEST, 0);
470
471 class App : public TestNanoapp {
472 public:
473 App()
474 : TestNanoapp(
475 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
476
477 void handleEvent(uint32_t, uint16_t eventType,
478 const void *eventData) override {
479 switch (eventType) {
480 case CHRE_EVENT_TEST_EVENT: {
481 auto event = static_cast<const TestEvent *>(eventData);
482 switch (event->type) {
483 case LISTENER_REQUEST: {
484 auto enable = *(static_cast<const bool *>(event->data));
485 const bool success =
486 chreGnssConfigurePassiveLocationListener(enable);
487 TestEventQueueSingleton::get()->pushEvent(LISTENER_REQUEST,
488 success);
489 break;
490 }
491 }
492 }
493 }
494 }
495 };
496
497 uint64_t appId = loadNanoapp(MakeUnique<App>());
498
499 bool success;
500 EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled());
501
502 sendEventToNanoapp(appId, LISTENER_REQUEST, true);
503 waitForEvent(LISTENER_REQUEST, &success);
504 EXPECT_TRUE(success);
505 EXPECT_TRUE(chrePalGnssIsPassiveLocationListenerEnabled());
506
507 sendEventToNanoapp(appId, LISTENER_REQUEST, false);
508 waitForEvent(LISTENER_REQUEST, &success);
509 EXPECT_TRUE(success);
510 EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled());
511 }
512
TEST_F(GnssTest,GnssUnsubscribeToPassiveListenerOnUnload)513 TEST_F(GnssTest, GnssUnsubscribeToPassiveListenerOnUnload) {
514 CREATE_CHRE_TEST_EVENT(LISTENER_REQUEST, 0);
515
516 class App : public TestNanoapp {
517 public:
518 App()
519 : TestNanoapp(
520 TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_GNSS}) {}
521
522 void handleEvent(uint32_t, uint16_t eventType,
523 const void *eventData) override {
524 switch (eventType) {
525 case CHRE_EVENT_TEST_EVENT: {
526 auto event = static_cast<const TestEvent *>(eventData);
527 switch (event->type) {
528 case LISTENER_REQUEST: {
529 auto enable = *(static_cast<const bool *>(event->data));
530 const bool success =
531 chreGnssConfigurePassiveLocationListener(enable);
532 TestEventQueueSingleton::get()->pushEvent(LISTENER_REQUEST,
533 success);
534 }
535 }
536 }
537 }
538 }
539 };
540
541 uint64_t appId = loadNanoapp(MakeUnique<App>());
542
543 EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled());
544
545 sendEventToNanoapp(appId, LISTENER_REQUEST, true);
546 bool success;
547 waitForEvent(LISTENER_REQUEST, &success);
548 EXPECT_TRUE(success);
549 EXPECT_TRUE(chrePalGnssIsPassiveLocationListenerEnabled());
550
551 unloadNanoapp(appId);
552 EXPECT_FALSE(chrePalGnssIsPassiveLocationListenerEnabled());
553 }
554
555 } // namespace
556 } // namespace chre