• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 package com.android.experimentalcar;
18 
19 import static com.android.experimentalcar.DriverDistractionExperimentalFeatureService.DEFAULT_AWARENESS_PERCENTAGE;
20 import static com.android.experimentalcar.DriverDistractionExperimentalFeatureService.DISPATCH_THROTTLE_MS;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.anyLong;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.spy;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.car.Car;
34 import android.car.VehiclePropertyIds;
35 import android.car.experimental.CarDriverDistractionManager;
36 import android.car.experimental.DriverAwarenessEvent;
37 import android.car.experimental.DriverAwarenessSupplierConfig;
38 import android.car.experimental.DriverAwarenessSupplierService;
39 import android.car.experimental.DriverDistractionChangeEvent;
40 import android.car.experimental.IDriverAwarenessSupplier;
41 import android.car.experimental.IDriverAwarenessSupplierCallback;
42 import android.car.hardware.CarPropertyValue;
43 import android.content.ComponentName;
44 import android.content.Context;
45 import android.content.ContextWrapper;
46 import android.content.Intent;
47 import android.content.ServiceConnection;
48 import android.content.pm.PackageManager;
49 import android.content.res.Resources;
50 import android.hardware.input.InputManager;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Looper;
54 import android.os.RemoteException;
55 import android.os.UserHandle;
56 import android.util.Pair;
57 import android.view.InputChannel;
58 import android.view.InputMonitor;
59 
60 import androidx.test.InstrumentationRegistry;
61 import androidx.test.core.app.ApplicationProvider;
62 import androidx.test.rule.ServiceTestRule;
63 
64 import com.android.internal.annotations.GuardedBy;
65 
66 import org.junit.After;
67 import org.junit.Before;
68 import org.junit.Ignore;
69 import org.junit.Rule;
70 import org.junit.Test;
71 import org.junit.runner.RunWith;
72 import org.mockito.Mock;
73 import org.mockito.junit.MockitoJUnitRunner;
74 
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.Collections;
78 import java.util.List;
79 import java.util.concurrent.Semaphore;
80 import java.util.concurrent.TimeUnit;
81 import java.util.stream.Collectors;
82 
83 @RunWith(MockitoJUnitRunner.class)
84 public class DriverDistractionExperimentalFeatureServiceTest {
85 
86     private static final String TAG = "Car.DriverDistractionServiceTest";
87 
88     private static final String SERVICE_BIND_GAZE_SUPPLIER =
89             "com.android.experimentalcar/.GazeDriverAwarenessSupplier";
90 
91     private static final long INITIAL_TIME = 1000L;
92     private static final long PREFERRED_SUPPLIER_STALENESS = 10L;
93 
94     private final IDriverAwarenessSupplier mFallbackSupplier =
95             new IDriverAwarenessSupplier.Stub() {
96                 @Override
97                 public void onReady() throws RemoteException {
98                 }
99 
100                 @Override
101                 public void setCallback(IDriverAwarenessSupplierCallback callback)
102                         throws RemoteException {
103                 }
104             };
105     private final DriverAwarenessSupplierConfig mFallbackConfig = new DriverAwarenessSupplierConfig(
106             DriverAwarenessSupplierService.NO_STALENESS);
107 
108     private final IDriverAwarenessSupplier mPreferredSupplier =
109             new IDriverAwarenessSupplier.Stub() {
110                 @Override
111                 public void onReady() throws RemoteException {
112                 }
113 
114                 @Override
115                 public void setCallback(IDriverAwarenessSupplierCallback callback)
116                         throws RemoteException {
117                 }
118             };
119     private final DriverAwarenessSupplierConfig mPreferredSupplierConfig =
120             new DriverAwarenessSupplierConfig(PREFERRED_SUPPLIER_STALENESS);
121 
122     // stores the last change event from OnDriverDistractionChange call
123     private DriverDistractionChangeEvent mLastDistractionEvent;
124     private List<DriverDistractionChangeEvent> mDistractionEventHistory = new ArrayList<>();
125     private final Semaphore mChangeEventSignal = new Semaphore(0);
126 
127     private final CarDriverDistractionManager.OnDriverDistractionChangeListener mChangeListener =
128             new CarDriverDistractionManager.OnDriverDistractionChangeListener() {
129                 @Override
130                 public void onDriverDistractionChange(DriverDistractionChangeEvent event) {
131                     // should be dispatched to main thread.
132                     assertThat(Looper.getMainLooper()).isEqualTo(Looper.myLooper());
133                     mLastDistractionEvent = event;
134                     mDistractionEventHistory.add(event);
135                     mChangeEventSignal.release();
136                 }
137             };
138 
139     @Mock
140     private InputManager mInputManager;
141 
142     @Mock
143     private InputMonitor mInputMonitor;
144 
145     @Mock
146     private IBinder mIBinder;
147 
148     @Mock
149     private Handler mHandler;
150 
151     @Rule
152     public final ServiceTestRule serviceRule = new ServiceTestRule();
153 
154     private final Context mSpyContext = spy(
155             InstrumentationRegistry.getInstrumentation().getContext());
156 
157     private DriverDistractionExperimentalFeatureService mService;
158     private CarDriverDistractionManager mManager;
159     private FakeTimeSource mTimeSource;
160     private FakeTimer mExpiredAwarenessTimer;
161     private List<Runnable> mQueuedRunnables;
162 
163     @Before
setUp()164     public void setUp() throws Exception {
165         mTimeSource = new FakeTimeSource(INITIAL_TIME);
166         mExpiredAwarenessTimer = new FakeTimer();
167         // execute all handler posts immediately
168         when(mHandler.post(any())).thenAnswer(i -> {
169             ((Runnable) i.getArguments()[0]).run();
170             return true;
171         });
172         // keep track of runnables with delayed posts
173         mQueuedRunnables = new ArrayList<>();
174         when(mHandler.postDelayed(any(), anyLong())).thenAnswer(i -> {
175             mQueuedRunnables.add(((Runnable) i.getArguments()[0]));
176             return true;
177         });
178         mDistractionEventHistory = new ArrayList<>();
179         doReturn(PackageManager.PERMISSION_GRANTED)
180                 .when(mSpyContext).checkCallingOrSelfPermission(any());
181         mService = new DriverDistractionExperimentalFeatureService(mSpyContext, mTimeSource,
182                 mExpiredAwarenessTimer, Looper.myLooper(), mHandler);
183         // Car must not be created with a mock context (otherwise CarService may crash)
184         mManager = new CarDriverDistractionManager(Car.createCar(mSpyContext), mService);
185     }
186 
187     @After
tearDown()188     public void tearDown() throws Exception {
189         if (mService != null) {
190             mService.release();
191         }
192         resetChangeEventWait();
193     }
194 
195     @Test
testConfig_servicesCanBeBound()196     public void testConfig_servicesCanBeBound() throws Exception {
197         Context realContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
198 
199         // Get the actual suppliers defined in the config
200         String[] preferredDriverAwarenessSuppliers = realContext.getResources().getStringArray(
201                 R.array.preferredDriverAwarenessSuppliers);
202 
203         for (String supplierStringName : preferredDriverAwarenessSuppliers) {
204             ComponentName supplierComponent = ComponentName.unflattenFromString(supplierStringName);
205             Class<?> supplerClass = Class.forName(supplierComponent.getClassName());
206             Intent serviceIntent =
207                     new Intent(ApplicationProvider.getApplicationContext(), supplerClass);
208 
209             // Bind the service and grab a reference to the binder.
210             IBinder binder = serviceRule.bindService(serviceIntent);
211 
212             assertThat(binder instanceof DriverAwarenessSupplierService.SupplierBinder).isTrue();
213         }
214     }
215 
216     @Test
testInit_bindsToServicesInXmlConfig()217     public void testInit_bindsToServicesInXmlConfig() throws Exception {
218         Context spyContext = spy(InstrumentationRegistry.getInstrumentation().getTargetContext());
219 
220         // Mock the config to load a gaze supplier
221         Resources spyResources = spy(spyContext.getResources());
222         doReturn(spyResources).when(spyContext).getResources();
223         doReturn(new String[]{SERVICE_BIND_GAZE_SUPPLIER}).when(spyResources).getStringArray(
224                 anyInt());
225 
226         // Mock the InputManager that will be used by TouchDriverAwarenessSupplier
227         doReturn(mInputManager).when(spyContext).getSystemService(Context.INPUT_SERVICE);
228         when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor);
229         // InputChannel cannot be mocked because it passes to InputEventReceiver.
230         final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
231         inputChannels[0].dispose();
232         when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
233 
234         // Create a special context that allows binders to succeed and keeps track of them. Doesn't
235         // actually start the intents / services.
236         ServiceLauncherContext serviceLauncherContext = new ServiceLauncherContext(spyContext);
237         mService = new DriverDistractionExperimentalFeatureService(serviceLauncherContext,
238                 mTimeSource, mExpiredAwarenessTimer,
239                 spyContext.getMainLooper(), mHandler);
240         mService.init();
241 
242         serviceLauncherContext.assertBoundService(SERVICE_BIND_GAZE_SUPPLIER);
243 
244         serviceLauncherContext.reset();
245         inputChannels[0].dispose();
246     }
247 
248     @Test
testHandleDriverAwarenessEvent_updatesCurrentValue_withLatestEvent()249     public void testHandleDriverAwarenessEvent_updatesCurrentValue_withLatestEvent()
250             throws Exception {
251         mService.setDriverAwarenessSuppliers(Arrays.asList(
252                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
253 
254         float firstAwarenessValue = 0.7f;
255         emitDriverAwarenessEvent(mFallbackSupplier, INITIAL_TIME + 1, firstAwarenessValue);
256 
257         assertThat(getCurrentAwarenessValue()).isEqualTo(firstAwarenessValue);
258     }
259 
260     @Test
testHandleDriverAwarenessEvent_hasPreferredEvent_ignoresFallbackEvent()261     public void testHandleDriverAwarenessEvent_hasPreferredEvent_ignoresFallbackEvent()
262             throws Exception {
263         mService.setDriverAwarenessSuppliers(Arrays.asList(
264                 new Pair<>(mFallbackSupplier, mFallbackConfig),
265                 new Pair<>(mPreferredSupplier, mPreferredSupplierConfig)));
266 
267         // emit an event from the preferred supplier before the fallback supplier
268         float preferredValue = 0.6f;
269         emitDriverAwarenessEvent(mPreferredSupplier, INITIAL_TIME + 1, preferredValue);
270         float fallbackValue = 0.7f;
271         emitDriverAwarenessEvent(mFallbackSupplier, INITIAL_TIME + 2, fallbackValue);
272 
273         // even though the fallback supplier has a more recent timestamp, it is not the current
274         // since the event from the preferred supplier is still fresh
275         assertThat(getCurrentAwarenessValue()).isEqualTo(preferredValue);
276     }
277 
278     @Test
testHandleDriverAwarenessEvent_ignoresOldEvents()279     public void testHandleDriverAwarenessEvent_ignoresOldEvents() throws Exception {
280         mService.setDriverAwarenessSuppliers(Arrays.asList(
281                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
282 
283         float firstAwarenessValue = 0.7f;
284         emitDriverAwarenessEvent(mFallbackSupplier, INITIAL_TIME + 1, firstAwarenessValue);
285         long oldTime = INITIAL_TIME - 100;
286         emitDriverAwarenessEvent(mFallbackSupplier, oldTime, 0.6f);
287 
288         // the event with the old timestamp shouldn't overwrite the value with a more recent
289         // timestamp
290         assertThat(getCurrentAwarenessValue()).isEqualTo(firstAwarenessValue);
291     }
292 
293     @Test
testPreferredAwarenessEvent_becomesStale_fallsBackToFallbackEvent()294     public void testPreferredAwarenessEvent_becomesStale_fallsBackToFallbackEvent()
295             throws Exception {
296         setVehicleMoving();
297         mService.setDriverAwarenessSuppliers(Arrays.asList(
298                 new Pair<>(mFallbackSupplier, mFallbackConfig),
299                 new Pair<>(mPreferredSupplier, mPreferredSupplierConfig)));
300 
301         // emit an event from the preferred supplier before the fallback supplier
302         float preferredSupplierAwarenessValue = 0.6f;
303         long preferredSupplierEventTime = INITIAL_TIME + 1;
304         mTimeSource.setTimeMillis(preferredSupplierEventTime);
305         emitDriverAwarenessEvent(mPreferredSupplier, preferredSupplierEventTime,
306                 preferredSupplierAwarenessValue);
307         float fallbackSuppplierAwarenessValue = 0.7f;
308         long fallbackSupplierEventTime = INITIAL_TIME + 2;
309         mTimeSource.setTimeMillis(fallbackSupplierEventTime);
310         emitDriverAwarenessEvent(mFallbackSupplier, fallbackSupplierEventTime,
311                 fallbackSuppplierAwarenessValue);
312 
313         // the preferred supplier still has a fresh event
314         assertThat(getCurrentAwarenessValue()).isEqualTo(preferredSupplierAwarenessValue);
315 
316         // go into the future
317         mTimeSource.setTimeMillis(
318                 preferredSupplierEventTime + PREFERRED_SUPPLIER_STALENESS + 1);
319         mExpiredAwarenessTimer.executePendingTask();
320 
321         // the preferred supplier's data has become stale
322         assertThat(getCurrentAwarenessValue()).isEqualTo(fallbackSuppplierAwarenessValue);
323         assertThat(mService.getLastDistractionEvent()).isEqualTo(
324                 new DriverDistractionChangeEvent.Builder()
325                         // time is when the event expired
326                         .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime())
327                         .setAwarenessPercentage(fallbackSuppplierAwarenessValue)
328                         .build());
329     }
330 
331 
332     @Test
testGetLastDistractionEvent_noEvents_returnsDefault()333     public void testGetLastDistractionEvent_noEvents_returnsDefault() throws Exception {
334         assertThat(mService.getLastDistractionEvent()).isEqualTo(
335                 new DriverDistractionChangeEvent.Builder()
336                         .setElapsedRealtimeTimestamp(INITIAL_TIME)
337                         .setAwarenessPercentage(DEFAULT_AWARENESS_PERCENTAGE)
338                         .build());
339     }
340 
341     @Test
testGetLastDistractionEvent_afterEventEmit_returnsLastEvent()342     public void testGetLastDistractionEvent_afterEventEmit_returnsLastEvent() throws Exception {
343         setVehicleMoving();
344         mService.setDriverAwarenessSuppliers(Arrays.asList(
345                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
346 
347         float firstAwarenessValue = 0.7f;
348         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
349         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
350                 firstAwarenessValue);
351 
352         assertThat(mService.getLastDistractionEvent()).isEqualTo(
353                 new DriverDistractionChangeEvent.Builder()
354                         .setElapsedRealtimeTimestamp(INITIAL_TIME + 1)
355                         .setAwarenessPercentage(0.7f)
356                         .build());
357     }
358 
359     @Test
testManagerRegister_returnsInitialEvent()360     public void testManagerRegister_returnsInitialEvent() throws Exception {
361         long eventWaitTimeMs = 300;
362 
363         mService.setDriverAwarenessSuppliers(Arrays.asList(
364                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
365         resetChangeEventWait();
366         mManager.addDriverDistractionChangeListener(mChangeListener);
367 
368         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
369         assertThat(mLastDistractionEvent).isEqualTo(
370                 new DriverDistractionChangeEvent.Builder()
371                         .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime())
372                         .setAwarenessPercentage(DEFAULT_AWARENESS_PERCENTAGE)
373                         .build());
374     }
375 
376     @Test
testManagerRegister_distractionValueUnchanged_doesNotEmitEvent()377     public void testManagerRegister_distractionValueUnchanged_doesNotEmitEvent() throws Exception {
378         setVehicleMoving();
379         long eventWaitTimeMs = 300;
380 
381         mService.setDriverAwarenessSuppliers(Arrays.asList(
382                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
383         resetChangeEventWait();
384         mManager.addDriverDistractionChangeListener(mChangeListener);
385 
386         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
387         assertThat(mLastDistractionEvent).isEqualTo(
388                 new DriverDistractionChangeEvent.Builder()
389                         .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime())
390                         .setAwarenessPercentage(DEFAULT_AWARENESS_PERCENTAGE)
391                         .build());
392 
393         float firstAwarenessValue = 1.0f;
394         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
395         resetChangeEventWait();
396         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
397                 firstAwarenessValue);
398 
399         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isFalse();
400     }
401 
402     @Test
testManagerRegister_eventInThrottleWindow_isQueued()403     public void testManagerRegister_eventInThrottleWindow_isQueued()
404             throws Exception {
405         setVehicleMoving();
406         long eventWaitTimeMs = 300;
407 
408         mService.setDriverAwarenessSuppliers(Collections.singletonList(
409                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
410         resetChangeEventWait();
411         mManager.addDriverDistractionChangeListener(mChangeListener);
412         // 0th event: event from registering
413         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
414 
415         // 1st event: driver awareness changes, and first event is emitted
416         resetChangeEventWait();
417         float firstAwarenessValue = 0.9f;
418         long firstEventTime = INITIAL_TIME + 1;
419         mTimeSource.setTimeMillis(firstEventTime);
420         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
421                 firstAwarenessValue);
422         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
423 
424         // 2nd event: driver awareness changes within throttle window, so dispatch is scheduled
425         resetChangeEventWait();
426         float secondAwarenessValue = 0.8f;
427         long secondEventTime = INITIAL_TIME + 2;
428         mTimeSource.setTimeMillis(secondEventTime);
429         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
430                 secondAwarenessValue);
431 
432         // delayed runnable should be posted to handler in 1 second less than the throttle delay
433         // (since there was 1 second between the two events). No event should have been emitted.
434         verify(mHandler).postDelayed(any(), eq(DISPATCH_THROTTLE_MS - 1));
435         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isFalse();
436 
437         // event is emitted once the pending task is run
438         mQueuedRunnables.get(0).run();
439         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
440         assertThat(mLastDistractionEvent).isEqualTo(
441                 new DriverDistractionChangeEvent.Builder()
442                         .setElapsedRealtimeTimestamp(secondEventTime)
443                         .setAwarenessPercentage(secondAwarenessValue)
444                         .build());
445     }
446 
447     @Test
testManagerRegister_multipleEventsInThrottleWindow_dropsExtraEvents()448     public void testManagerRegister_multipleEventsInThrottleWindow_dropsExtraEvents()
449             throws Exception {
450         setVehicleMoving();
451         long eventWaitTimeMs = 300;
452 
453         mService.setDriverAwarenessSuppliers(Collections.singletonList(
454                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
455         resetChangeEventWait();
456         mManager.addDriverDistractionChangeListener(mChangeListener);
457         // 0th event: event from registering
458         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
459 
460         // 1st event: driver awareness changes, and first event is emitted
461         resetChangeEventWait();
462         float firstAwarenessValue = 0.9f;
463         long firstEventTime = INITIAL_TIME + 1;
464         mTimeSource.setTimeMillis(firstEventTime);
465         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
466                 firstAwarenessValue);
467         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
468 
469         // 2nd event: driver awareness changes within throttle window, so dispatch is scheduled
470         resetChangeEventWait();
471         float secondAwarenessValue = 0.8f;
472         long secondEventTime = INITIAL_TIME + 2;
473         mTimeSource.setTimeMillis(secondEventTime);
474         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
475                 secondAwarenessValue);
476         // runnable will be posted in 1 second less than the throttle delay
477         verify(mHandler).postDelayed(any(), eq(DISPATCH_THROTTLE_MS - 1));
478 
479         // 3rd event: driver awareness changes within throttle window again, no new schedule
480         float thirdAwarenessValue = 0.7f;
481         long thirdEventTime = INITIAL_TIME + 3;
482         mTimeSource.setTimeMillis(thirdEventTime);
483         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
484                 thirdAwarenessValue);
485         // verify that this was still only called once
486         verify(mHandler).postDelayed(any(), eq(DISPATCH_THROTTLE_MS - 1));
487 
488         // neither 2nd or 3rd events should trigger a callback
489         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isFalse();
490     }
491 
492     @Ignore("TODO(b/201654588): fix it")
493     @Test
testManagerRegister_multipleEventsOutsideThrottleWindow_emitsAllEvents()494     public void testManagerRegister_multipleEventsOutsideThrottleWindow_emitsAllEvents()
495             throws Exception {
496         setVehicleMoving();
497         long eventWaitTimeMs = 300;
498 
499         mService.setDriverAwarenessSuppliers(Collections.singletonList(
500                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
501         resetChangeEventWait();
502         mManager.addDriverDistractionChangeListener(mChangeListener);
503         // 0th event: event from registering
504         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
505 
506         // 1st event: driver awareness changes, and first event is emitted
507         resetChangeEventWait();
508         float firstAwarenessValue = 0.9f;
509         long firstEventTime = INITIAL_TIME + DISPATCH_THROTTLE_MS;
510         mTimeSource.setTimeMillis(firstEventTime);
511         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
512                 firstAwarenessValue);
513         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
514 
515         // 2nd event: outside throttle window, so dispatched
516         resetChangeEventWait();
517         float secondAwarenessValue = 0.8f;
518         long secondEventTime = INITIAL_TIME + DISPATCH_THROTTLE_MS * 2;
519         mTimeSource.setTimeMillis(secondEventTime);
520         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
521                 secondAwarenessValue);
522         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
523 
524         // 3rd event: outside throttle window, so dispatched
525         resetChangeEventWait();
526         float thirdAwarenessValue = 0.7f;
527         long thirdEventTime = INITIAL_TIME + DISPATCH_THROTTLE_MS * 3;
528         mTimeSource.setTimeMillis(thirdEventTime);
529         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
530                 thirdAwarenessValue);
531         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
532 
533         // all events should be in history
534         assertThat(mDistractionEventHistory).containsExactly(
535                 new DriverDistractionChangeEvent.Builder()
536                         .setElapsedRealtimeTimestamp(INITIAL_TIME)
537                         .setAwarenessPercentage(DEFAULT_AWARENESS_PERCENTAGE)
538                         .build(),
539                 new DriverDistractionChangeEvent.Builder()
540                         .setElapsedRealtimeTimestamp(firstEventTime)
541                         .setAwarenessPercentage(firstAwarenessValue)
542                         .build(),
543                 new DriverDistractionChangeEvent.Builder()
544                         .setElapsedRealtimeTimestamp(secondEventTime)
545                         .setAwarenessPercentage(secondAwarenessValue)
546                         .build(),
547                 new DriverDistractionChangeEvent.Builder()
548                         .setElapsedRealtimeTimestamp(thirdEventTime)
549                         .setAwarenessPercentage(thirdAwarenessValue)
550                         .build());
551     }
552 
553     @Test
testManagerRegister_receivesChangeEvents()554     public void testManagerRegister_receivesChangeEvents() throws Exception {
555         setVehicleMoving();
556         long eventWaitTimeMs = 300;
557 
558         mService.setDriverAwarenessSuppliers(Arrays.asList(
559                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
560         resetChangeEventWait();
561         mManager.addDriverDistractionChangeListener(mChangeListener);
562 
563         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
564         assertThat(mLastDistractionEvent).isEqualTo(
565                 new DriverDistractionChangeEvent.Builder()
566                         .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime())
567                         .setAwarenessPercentage(DEFAULT_AWARENESS_PERCENTAGE)
568                         .build());
569 
570         float firstAwarenessValue = 0.7f;
571         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
572         resetChangeEventWait();
573         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
574                 firstAwarenessValue);
575 
576         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isTrue();
577         assertThat(mLastDistractionEvent).isEqualTo(
578                 new DriverDistractionChangeEvent.Builder()
579                         .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime())
580                         .setAwarenessPercentage(firstAwarenessValue)
581                         .build());
582     }
583 
584     @Test
testManagerRegisterUnregister_stopsReceivingEvents()585     public void testManagerRegisterUnregister_stopsReceivingEvents() throws Exception {
586         long eventWaitTimeMs = 300;
587 
588         mService.setDriverAwarenessSuppliers(Arrays.asList(
589                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
590         resetChangeEventWait();
591         mManager.addDriverDistractionChangeListener(mChangeListener);
592         mManager.removeDriverDistractionChangeListener(mChangeListener);
593 
594         float firstAwarenessValue = 0.8f;
595         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
596         resetChangeEventWait();
597         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
598                 firstAwarenessValue);
599 
600         assertThat(waitForCallbackEvent(eventWaitTimeMs)).isFalse();
601     }
602 
603     @Test
testManagerUnregister_beforeRegister_doesNothing()604     public void testManagerUnregister_beforeRegister_doesNothing() throws Exception {
605         mManager.removeDriverDistractionChangeListener(mChangeListener);
606     }
607 
608     @Test
testDistractionEvent_noSpeedEventsReceived_awarenessPercentageRemainsFull()609     public void testDistractionEvent_noSpeedEventsReceived_awarenessPercentageRemainsFull()
610             throws Exception {
611         mService.setDriverAwarenessSuppliers(Arrays.asList(
612                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
613 
614         float firstAwarenessValue = 0.7f;
615         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
616         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
617                 firstAwarenessValue);
618 
619         // No new distraction event since required awareness is 0 until a speed change occurs
620         assertThat(mService.getLastDistractionEvent()).isEqualTo(
621                 new DriverDistractionChangeEvent.Builder()
622                         .setElapsedRealtimeTimestamp(INITIAL_TIME)
623                         .setAwarenessPercentage(1.0f)
624                         .build());
625     }
626 
627     @Test
testRequiredAwareness_multipleSpeedChanges_emitsSingleEvent()628     public void testRequiredAwareness_multipleSpeedChanges_emitsSingleEvent()
629             throws Exception {
630         setVehicleStopped();
631         mService.setDriverAwarenessSuppliers(Arrays.asList(
632                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
633 
634         float firstAwarenessValue = 0.7f;
635         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
636         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
637                 firstAwarenessValue);
638         mService.handleSpeedEventLocked(
639                 new CarPropertyValue<>(VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 30.0f));
640 
641         // Receive the first speed change event
642         assertThat(mService.getLastDistractionEvent()).isEqualTo(
643                 new DriverDistractionChangeEvent.Builder()
644                         .setElapsedRealtimeTimestamp(INITIAL_TIME + 1)
645                         .setAwarenessPercentage(0.7f)
646                         .build());
647 
648         mTimeSource.setTimeMillis(INITIAL_TIME + 2);
649         mService.handleSpeedEventLocked(
650                 new CarPropertyValue<>(VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 20.0f));
651         mTimeSource.setTimeMillis(INITIAL_TIME + 3);
652         mService.handleSpeedEventLocked(
653                 new CarPropertyValue<>(VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 40.0f));
654 
655         // Speed changes when already in a moving state don't trigger a new distraction event
656         assertThat(mService.getLastDistractionEvent()).isEqualTo(
657                 new DriverDistractionChangeEvent.Builder()
658                         .setElapsedRealtimeTimestamp(INITIAL_TIME + 1)
659                         .setAwarenessPercentage(0.7f)
660                         .build());
661     }
662 
663     @Test
testDistractionEvent_vehicleBecomesStopped_emitsFullAwareness()664     public void testDistractionEvent_vehicleBecomesStopped_emitsFullAwareness() throws Exception {
665         mService.setDriverAwarenessSuppliers(Arrays.asList(
666                 new Pair<>(mFallbackSupplier, mFallbackConfig)));
667 
668         setVehicleMoving();
669         float firstAwarenessValue = 0.7f;
670         mTimeSource.setTimeMillis(INITIAL_TIME + 1);
671         emitDriverAwarenessEvent(mFallbackSupplier, mTimeSource.elapsedRealtime(),
672                 firstAwarenessValue);
673 
674         mTimeSource.setTimeMillis(INITIAL_TIME + 2);
675         setVehicleStopped();
676 
677         // Awareness percentage is 1.0 since the vehicle is stopped, even though driver awareness
678         // is 0.7
679         assertThat(mService.getLastDistractionEvent()).isEqualTo(
680                 new DriverDistractionChangeEvent.Builder()
681                         .setElapsedRealtimeTimestamp(INITIAL_TIME + 2)
682                         .setAwarenessPercentage(1.0f)
683                         .build());
684     }
685 
setVehicleMoving()686     private void setVehicleMoving() {
687         mService.handleSpeedEventLocked(
688                 new CarPropertyValue<>(VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 30.0f));
689     }
690 
setVehicleStopped()691     private void setVehicleStopped() {
692         mService.handleSpeedEventLocked(
693                 new CarPropertyValue<>(VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 0.0f));
694     }
695 
resetChangeEventWait()696     private void resetChangeEventWait() {
697         mLastDistractionEvent = null;
698         mChangeEventSignal.drainPermits();
699     }
700 
waitForCallbackEvent(long timeoutMs)701     private boolean waitForCallbackEvent(long timeoutMs) {
702         boolean acquired = false;
703         try {
704             acquired = mChangeEventSignal.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
705         } catch (Exception ignored) {
706 
707         }
708         return acquired;
709     }
710 
getCurrentAwarenessValue()711     private float getCurrentAwarenessValue() {
712         return mService.getCurrentDriverAwareness().mAwarenessEvent.getAwarenessValue();
713     }
714 
715     /**
716      * Handle an event as if it were emitted from the specified supplier with the specified time and
717      * value.
718      */
emitDriverAwarenessEvent(IDriverAwarenessSupplier supplier, long time, float value)719     private void emitDriverAwarenessEvent(IDriverAwarenessSupplier supplier, long time, float value)
720             throws RemoteException {
721         long maxStaleness;
722         if (supplier == mFallbackSupplier) {
723             maxStaleness = DriverAwarenessSupplierService.NO_STALENESS;
724         } else {
725             maxStaleness = PREFERRED_SUPPLIER_STALENESS;
726         }
727         mService.handleDriverAwarenessEvent(
728                 new DriverDistractionExperimentalFeatureService.DriverAwarenessEventWrapper(
729                         new DriverAwarenessEvent(time, value),
730                         supplier,
731                         maxStaleness));
732     }
733 
734 
735     /** Overrides framework behavior to succeed on binding/starting processes. */
736     public class ServiceLauncherContext extends ContextWrapper {
737         private final Object mLock = new Object();
738 
739         @GuardedBy("mLock")
740         private List<Intent> mBoundIntents = new ArrayList<>();
741 
ServiceLauncherContext(Context base)742         ServiceLauncherContext(Context base) {
743             super(base);
744         }
745 
746         @Override
bindServiceAsUser(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user)747         public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
748                 Handler handler, UserHandle user) {
749             synchronized (mLock) {
750                 mBoundIntents.add(service);
751             }
752             conn.onServiceConnected(service.getComponent(), mIBinder);
753             return true;
754         }
755 
756         @Override
bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user)757         public boolean bindServiceAsUser(Intent service, ServiceConnection conn,
758                 int flags, UserHandle user) {
759             return bindServiceAsUser(service, conn, flags, null, user);
760         }
761 
762         @Override
unbindService(ServiceConnection conn)763         public void unbindService(ServiceConnection conn) {
764             // do nothing
765         }
766 
assertBoundService(String service)767         void assertBoundService(String service) {
768             synchronized (mLock) {
769                 assertThat(mBoundIntents.stream().map(Intent::getComponent).collect(
770                         Collectors.toList())).contains(ComponentName.unflattenFromString(service));
771             }
772         }
773 
reset()774         void reset() {
775             synchronized (mLock) {
776                 mBoundIntents.clear();
777             }
778         }
779     }
780 }
781