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