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 android.annotation.FloatRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.car.Car; 23 import android.car.VehiclePropertyIds; 24 import android.car.experimental.DriverAwarenessEvent; 25 import android.car.experimental.DriverAwarenessSupplierConfig; 26 import android.car.experimental.DriverAwarenessSupplierService; 27 import android.car.experimental.DriverDistractionChangeEvent; 28 import android.car.experimental.ExperimentalCar; 29 import android.car.experimental.IDriverAwarenessSupplier; 30 import android.car.experimental.IDriverAwarenessSupplierCallback; 31 import android.car.experimental.IDriverDistractionChangeListener; 32 import android.car.experimental.IDriverDistractionManager; 33 import android.car.hardware.CarPropertyValue; 34 import android.car.hardware.property.CarPropertyEvent; 35 import android.car.hardware.property.CarPropertyManager; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.ServiceConnection; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.RemoteCallbackList; 45 import android.os.RemoteException; 46 import android.os.UserHandle; 47 import android.util.Log; 48 import android.util.Pair; 49 50 import com.android.car.CarServiceBase; 51 import com.android.car.Utils; 52 import com.android.internal.annotations.GuardedBy; 53 import com.android.internal.annotations.VisibleForTesting; 54 55 import java.io.PrintWriter; 56 import java.util.ArrayDeque; 57 import java.util.ArrayList; 58 import java.util.Comparator; 59 import java.util.HashMap; 60 import java.util.List; 61 import java.util.Map; 62 import java.util.TimerTask; 63 64 /** 65 * Driver Distraction Service for using the driver's awareness, the required awareness of the 66 * driving environment to expose APIs for the driver's current distraction level. 67 * 68 * <p>Allows the registration of multiple {@link IDriverAwarenessSupplier} so that higher accuracy 69 * signals can be used when possible, with a fallback to less accurate signals. The {@link 70 * TouchDriverAwarenessSupplier} is always set to the fallback implementation - it is configured 71 * to send change-events, so its data will not become stale. 72 */ 73 public final class DriverDistractionExperimentalFeatureService extends 74 IDriverDistractionManager.Stub implements CarServiceBase { 75 76 private static final String TAG = "CAR.DriverDistractionService"; 77 78 /** 79 * The minimum delay between dispatched distraction events, in milliseconds. 80 */ 81 @VisibleForTesting 82 static final long DISPATCH_THROTTLE_MS = 50L; 83 private static final float DEFAULT_AWARENESS_VALUE_FOR_LOG = 1.0f; 84 private static final float MOVING_REQUIRED_AWARENESS = 1.0f; 85 private static final float STATIONARY_REQUIRED_AWARENESS = 0.0f; 86 private static final int MAX_EVENT_LOG_COUNT = 50; 87 private static final int PROPERTY_UPDATE_RATE_HZ = 5; 88 @VisibleForTesting 89 static final float DEFAULT_AWARENESS_PERCENTAGE = 1.0f; 90 91 private final HandlerThread mClientDispatchHandlerThread; 92 private final Handler mClientDispatchHandler; 93 94 private final Object mLock = new Object(); 95 96 @GuardedBy("mLock") 97 private final ArrayDeque<Utils.TransitionLog> mTransitionLogs = new ArrayDeque<>(); 98 99 /** 100 * All the active service connections. 101 */ 102 @GuardedBy("mLock") 103 private final List<ServiceConnection> mServiceConnections = new ArrayList<>(); 104 105 /** 106 * The binder for each supplier. 107 */ 108 @GuardedBy("mLock") 109 private final Map<ComponentName, IDriverAwarenessSupplier> mSupplierBinders = new HashMap<>(); 110 111 /** 112 * The configuration for each supplier. 113 */ 114 @GuardedBy("mLock") 115 private final Map<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig> mSupplierConfigs = 116 new HashMap<>(); 117 118 /** 119 * List of driver awareness suppliers that can be used to understand the current driver 120 * awareness level. Ordered from highest to lowest priority. 121 */ 122 @GuardedBy("mLock") 123 private final List<IDriverAwarenessSupplier> mPrioritizedDriverAwarenessSuppliers = 124 new ArrayList<>(); 125 126 /** 127 * Helper map for looking up the priority rank of a supplier by name. A higher integer value 128 * represents a higher priority. 129 */ 130 @GuardedBy("mLock") 131 private final Map<IDriverAwarenessSupplier, Integer> mDriverAwarenessSupplierPriorities = 132 new HashMap<>(); 133 134 /** 135 * List of clients listening to UX restriction events. 136 */ 137 private final RemoteCallbackList<IDriverDistractionChangeListener> mDistractionClients = 138 new RemoteCallbackList<>(); 139 140 /** 141 * Comparator used to sort {@link #mDriverAwarenessSupplierPriorities}. 142 */ 143 private final Comparator<IDriverAwarenessSupplier> mPrioritizedSuppliersComparator = 144 (left, right) -> { 145 int leftPri = mDriverAwarenessSupplierPriorities.get(left); 146 int rightPri = mDriverAwarenessSupplierPriorities.get(right); 147 // sort descending 148 return rightPri - leftPri; 149 }; 150 151 /** 152 * Keep track of the most recent awareness event for each supplier for use when the data from 153 * higher priority suppliers becomes stale. This is necessary in order to seamlessly handle 154 * fallback scenarios when data from preferred providers becomes stale. 155 */ 156 @GuardedBy("mLock") 157 private final Map<IDriverAwarenessSupplier, DriverAwarenessEventWrapper> 158 mCurrentAwarenessEventsMap = 159 new HashMap<>(); 160 161 /** 162 * The awareness event that is currently being used to determine the driver awareness level. 163 * 164 * <p>This is null until it is set by the first awareness supplier to send an event 165 */ 166 @GuardedBy("mLock") 167 @Nullable 168 private DriverAwarenessEventWrapper mCurrentDriverAwareness; 169 170 /** 171 * Timer to alert when the current driver awareness event has become stale. 172 */ 173 @GuardedBy("mLock") 174 private ITimer mExpiredDriverAwarenessTimer; 175 176 /** 177 * The current, non-stale, driver distraction event. Defaults to 100% awareness. 178 */ 179 @GuardedBy("mLock") 180 private DriverDistractionChangeEvent mCurrentDistractionEvent; 181 182 /** 183 * The required driver awareness based on the current driving environment, where 1.0 means that 184 * full awareness is required and 0.0 means than no awareness is required. 185 */ 186 @FloatRange(from = 0.0f, to = 1.0f) 187 @GuardedBy("mLock") 188 private float mRequiredAwareness = STATIONARY_REQUIRED_AWARENESS; 189 190 @GuardedBy("mLock") 191 private Car mCar; 192 193 @GuardedBy("mLock") 194 private CarPropertyManager mPropertyManager; 195 196 /** 197 * The time that last event was emitted, measured in milliseconds since boot using the {@link 198 * android.os.SystemClock#uptimeMillis()} time-base. 199 */ 200 @GuardedBy("mLock") 201 private long mLastDispatchUptimeMillis; 202 203 /** 204 * Whether there is currently a pending dispatch to clients. 205 */ 206 @GuardedBy("mLock") 207 private boolean mIsDispatchQueued; 208 209 private final Context mContext; 210 private final ITimeSource mTimeSource; 211 private final Looper mLooper; 212 213 private final Runnable mDispatchCurrentDistractionRunnable = () -> { 214 synchronized (mLock) { 215 // dispatch whatever the current value is at this time in the future 216 dispatchCurrentDistractionEventToClientsLocked( 217 mCurrentDistractionEvent); 218 mIsDispatchQueued = false; 219 } 220 }; 221 222 /** 223 * Create an instance of {@link DriverDistractionExperimentalFeatureService}. 224 * 225 * @param context the context 226 */ DriverDistractionExperimentalFeatureService(Context context)227 DriverDistractionExperimentalFeatureService(Context context) { 228 this(context, new SystemTimeSource(), new SystemTimer(), Looper.myLooper(), null); 229 } 230 231 @VisibleForTesting DriverDistractionExperimentalFeatureService( Context context, ITimeSource timeSource, ITimer timer, Looper looper, Handler clientDispatchHandler)232 DriverDistractionExperimentalFeatureService( 233 Context context, 234 ITimeSource timeSource, 235 ITimer timer, 236 Looper looper, 237 Handler clientDispatchHandler) { 238 mContext = context; 239 mTimeSource = timeSource; 240 mExpiredDriverAwarenessTimer = timer; 241 mCurrentDistractionEvent = new DriverDistractionChangeEvent.Builder() 242 .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime()) 243 .setAwarenessPercentage(DEFAULT_AWARENESS_PERCENTAGE) 244 .build(); 245 mClientDispatchHandlerThread = new HandlerThread(TAG); 246 mClientDispatchHandlerThread.start(); 247 if (clientDispatchHandler == null) { 248 mClientDispatchHandler = new Handler(mClientDispatchHandlerThread.getLooper()); 249 } else { 250 mClientDispatchHandler = clientDispatchHandler; 251 } 252 mLooper = looper; 253 } 254 255 @Override init()256 public void init() { 257 // The touch supplier is an internal implementation, so it can be started initiated by its 258 // constructor, unlike other suppliers 259 ComponentName touchComponent = new ComponentName(mContext, 260 TouchDriverAwarenessSupplier.class); 261 TouchDriverAwarenessSupplier touchSupplier = new TouchDriverAwarenessSupplier(mContext, 262 new DriverAwarenessSupplierCallback(touchComponent), mLooper); 263 addDriverAwarenessSupplier(touchComponent, touchSupplier, /* priority= */ 0); 264 touchSupplier.onReady(); 265 266 String[] preferredDriverAwarenessSuppliers = mContext.getResources().getStringArray( 267 R.array.preferredDriverAwarenessSuppliers); 268 for (int i = 0; i < preferredDriverAwarenessSuppliers.length; i++) { 269 String supplierStringName = preferredDriverAwarenessSuppliers[i]; 270 ComponentName externalComponent = ComponentName.unflattenFromString(supplierStringName); 271 // the touch supplier has priority 0 and preferred suppliers are higher based on order 272 int priority = i + 1; 273 bindDriverAwarenessSupplierService(externalComponent, priority); 274 } 275 276 synchronized (mLock) { 277 mCar = Car.createCar(mContext); 278 if (mCar != null) { 279 mPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE); 280 } else { 281 Log.e(TAG, "Unable to connect to car in init"); 282 } 283 } 284 285 if (mPropertyManager != null) { 286 mPropertyManager.registerCallback(mSpeedPropertyEventCallback, 287 VehiclePropertyIds.PERF_VEHICLE_SPEED, 288 PROPERTY_UPDATE_RATE_HZ); 289 } else { 290 Log.e(TAG, "Unable to get car property service."); 291 } 292 } 293 294 @Override release()295 public void release() { 296 logd("release"); 297 mDistractionClients.kill(); 298 synchronized (mLock) { 299 mExpiredDriverAwarenessTimer.cancel(); 300 mClientDispatchHandler.removeCallbacksAndMessages(null); 301 for (ServiceConnection serviceConnection : mServiceConnections) { 302 mContext.unbindService(serviceConnection); 303 } 304 if (mPropertyManager != null) { 305 mPropertyManager.unregisterCallback(mSpeedPropertyEventCallback); 306 } 307 if (mCar != null) { 308 mCar.disconnect(); 309 } 310 } 311 } 312 313 @Override dump(PrintWriter writer)314 public void dump(PrintWriter writer) { 315 writer.println("*DriverDistractionExperimentalFeatureService*"); 316 mDistractionClients.dump(writer, "Distraction Clients "); 317 writer.println("Prioritized Driver Awareness Suppliers (highest to lowest priority):"); 318 synchronized (mLock) { 319 for (int i = 0; i < mPrioritizedDriverAwarenessSuppliers.size(); i++) { 320 writer.println( 321 String.format(" %d: %s", i, mPrioritizedDriverAwarenessSuppliers.get( 322 i).getClass().getName())); 323 } 324 writer.println("Current Driver Awareness:"); 325 writer.println(" Value: " 326 + (mCurrentDriverAwareness == null ? "unknown" 327 : mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue())); 328 writer.println(" Supplier: " + (mCurrentDriverAwareness == null ? "unknown" 329 : mCurrentDriverAwareness.mSupplier.getClass().getSimpleName())); 330 writer.println(" Timestamp (ms since boot): " 331 + (mCurrentDriverAwareness == null ? "unknown" 332 : mCurrentDriverAwareness.mAwarenessEvent.getTimeStamp())); 333 writer.println("Current Required Awareness: " + mRequiredAwareness); 334 writer.println("Last Distraction Event:"); 335 writer.println(" Value: " 336 + (mCurrentDistractionEvent == null ? "unknown" 337 : mCurrentDistractionEvent.getAwarenessPercentage())); 338 writer.println(" Timestamp (ms since boot): " 339 + (mCurrentDistractionEvent == null ? "unknown" 340 : mCurrentDistractionEvent.getElapsedRealtimeTimestamp())); 341 writer.println("Dispatch Status:"); 342 writer.println(" mLastDispatchUptimeMillis: " + mLastDispatchUptimeMillis); 343 writer.println(" mIsDispatchQueued: " + mIsDispatchQueued); 344 writer.println("Change log:"); 345 for (Utils.TransitionLog log : mTransitionLogs) { 346 writer.println(log); 347 } 348 } 349 } 350 351 /** 352 * Bind to a {@link DriverAwarenessSupplierService} by its component name. 353 * 354 * @param componentName the name of the {@link DriverAwarenessSupplierService} to bind to. 355 * @param priority the priority rank of this supplier 356 */ bindDriverAwarenessSupplierService(ComponentName componentName, int priority)357 private void bindDriverAwarenessSupplierService(ComponentName componentName, int priority) { 358 Intent intent = new Intent(); 359 intent.setComponent(componentName); 360 ServiceConnection connection = new DriverAwarenessServiceConnection(priority); 361 synchronized (mLock) { 362 mServiceConnections.add(connection); 363 } 364 if (!mContext.bindServiceAsUser(intent, connection, 365 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { 366 Log.e(TAG, "Unable to bind with intent: " + intent); 367 // TODO(b/146471650) attempt to rebind 368 } 369 } 370 371 @VisibleForTesting handleDriverAwarenessEvent(DriverAwarenessEventWrapper awarenessEventWrapper)372 void handleDriverAwarenessEvent(DriverAwarenessEventWrapper awarenessEventWrapper) { 373 synchronized (mLock) { 374 handleDriverAwarenessEventLocked(awarenessEventWrapper); 375 } 376 } 377 378 /** 379 * Handle the driver awareness event by: 380 * <ul> 381 * <li>Cache the driver awareness event for its supplier</li> 382 * <li>Update the current awareness value</li> 383 * <li>Register to refresh the awareness value again when the new current expires</li> 384 * </ul> 385 * 386 * @param awarenessEventWrapper the driver awareness event that has occurred 387 */ 388 @GuardedBy("mLock") handleDriverAwarenessEventLocked( DriverAwarenessEventWrapper awarenessEventWrapper)389 private void handleDriverAwarenessEventLocked( 390 DriverAwarenessEventWrapper awarenessEventWrapper) { 391 // update the current awareness event for the supplier, checking that it is the newest event 392 IDriverAwarenessSupplier supplier = awarenessEventWrapper.mSupplier; 393 long timestamp = awarenessEventWrapper.mAwarenessEvent.getTimeStamp(); 394 if (!mCurrentAwarenessEventsMap.containsKey(supplier) 395 || mCurrentAwarenessEventsMap.get(supplier).mAwarenessEvent.getTimeStamp() 396 < timestamp) { 397 mCurrentAwarenessEventsMap.put(awarenessEventWrapper.mSupplier, awarenessEventWrapper); 398 } 399 400 int oldSupplierPriority = mDriverAwarenessSupplierPriorities.get(supplier); 401 float oldAwarenessValue = DEFAULT_AWARENESS_VALUE_FOR_LOG; 402 if (mCurrentDriverAwareness != null) { 403 oldAwarenessValue = mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue(); 404 } 405 406 updateCurrentAwarenessValueLocked(); 407 408 int newSupplierPriority = mDriverAwarenessSupplierPriorities.get( 409 mCurrentDriverAwareness.mSupplier); 410 if (mSupplierConfigs.get(mCurrentDriverAwareness.mSupplier).getMaxStalenessMillis() 411 != DriverAwarenessSupplierService.NO_STALENESS 412 && newSupplierPriority >= oldSupplierPriority) { 413 // only reschedule an expiration if this is for a supplier that is the same or higher 414 // priority than the old value. If there is a higher priority supplier with non-stale 415 // data, then mCurrentDriverAwareness won't change even though we received a new event. 416 scheduleExpirationTimerLocked(); 417 } 418 419 if (oldAwarenessValue != mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue()) { 420 logd("Driver awareness updated: " 421 + mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue()); 422 addTransitionLogLocked(oldAwarenessValue, 423 awarenessEventWrapper.mAwarenessEvent.getAwarenessValue(), 424 "Driver awareness updated by " 425 + awarenessEventWrapper.mSupplier.getClass().getSimpleName()); 426 } 427 428 updateCurrentDistractionEventLocked(); 429 } 430 431 /** 432 * Get the current awareness value. 433 */ 434 @VisibleForTesting getCurrentDriverAwareness()435 DriverAwarenessEventWrapper getCurrentDriverAwareness() { 436 return mCurrentDriverAwareness; 437 } 438 439 /** 440 * Set the drier awareness suppliers. Allows circumventing the {@link #init()} logic. 441 */ 442 @VisibleForTesting setDriverAwarenessSuppliers( List<Pair<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig>> suppliers)443 void setDriverAwarenessSuppliers( 444 List<Pair<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig>> suppliers) { 445 mPrioritizedDriverAwarenessSuppliers.clear(); 446 mDriverAwarenessSupplierPriorities.clear(); 447 for (int i = 0; i < suppliers.size(); i++) { 448 Pair<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig> pair = suppliers.get(i); 449 mSupplierConfigs.put(pair.first, pair.second); 450 mDriverAwarenessSupplierPriorities.put(pair.first, i); 451 mPrioritizedDriverAwarenessSuppliers.add(pair.first); 452 } 453 mPrioritizedDriverAwarenessSuppliers.sort(mPrioritizedSuppliersComparator); 454 } 455 456 /** 457 * {@link CarPropertyEvent} listener registered with the {@link CarPropertyManager} for getting 458 * speed change notifications. 459 */ 460 private final CarPropertyManager.CarPropertyEventCallback mSpeedPropertyEventCallback = 461 new CarPropertyManager.CarPropertyEventCallback() { 462 @Override 463 public void onChangeEvent(CarPropertyValue value) { 464 synchronized (mLock) { 465 handleSpeedEventLocked(value); 466 } 467 } 468 469 @Override 470 public void onErrorEvent(int propId, int zone) { 471 Log.e(TAG, "Error in callback for vehicle speed"); 472 } 473 }; 474 475 476 @VisibleForTesting 477 @GuardedBy("mLock") handleSpeedEventLocked(@onNull CarPropertyValue value)478 void handleSpeedEventLocked(@NonNull CarPropertyValue value) { 479 if (value.getPropertyId() != VehiclePropertyIds.PERF_VEHICLE_SPEED) { 480 Log.e(TAG, "Unexpected property id: " + value.getPropertyId()); 481 return; 482 } 483 484 float oldValue = mRequiredAwareness; 485 if ((Float) value.getValue() > 0) { 486 mRequiredAwareness = MOVING_REQUIRED_AWARENESS; 487 } else { 488 mRequiredAwareness = STATIONARY_REQUIRED_AWARENESS; 489 } 490 491 if (Float.compare(oldValue, mRequiredAwareness) != 0) { 492 logd("Required awareness updated: " + mRequiredAwareness); 493 addTransitionLogLocked(oldValue, mRequiredAwareness, "Required awareness"); 494 updateCurrentDistractionEventLocked(); 495 } 496 } 497 498 @GuardedBy("mLock") updateCurrentDistractionEventLocked()499 private void updateCurrentDistractionEventLocked() { 500 if (mCurrentDriverAwareness == null) { 501 logd("Driver awareness level is not yet known"); 502 return; 503 } 504 float awarenessPercentage; 505 if (mRequiredAwareness == 0) { 506 // avoid divide by 0 error - awareness percentage should be 100% when required 507 // awareness is 0 508 awarenessPercentage = 1.0f; 509 } else { 510 // Cap awareness percentage at 100% 511 awarenessPercentage = Math.min( 512 mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue() 513 / mRequiredAwareness, 1.0f); 514 } 515 if (Float.compare(mCurrentDistractionEvent.getAwarenessPercentage(), awarenessPercentage) 516 == 0) { 517 // no need to dispatch unless there's a change 518 return; 519 } 520 521 addTransitionLogLocked(mCurrentDistractionEvent.getAwarenessPercentage(), 522 awarenessPercentage, "Awareness percentage"); 523 524 mCurrentDistractionEvent = new DriverDistractionChangeEvent.Builder() 525 .setElapsedRealtimeTimestamp(mTimeSource.elapsedRealtime()) 526 .setAwarenessPercentage(awarenessPercentage) 527 .build(); 528 529 long nowUptimeMillis = mTimeSource.uptimeMillis(); 530 if (shouldThrottleDispatchEventLocked(nowUptimeMillis)) { 531 scheduleAwarenessDispatchLocked(nowUptimeMillis); 532 } else { 533 // if event doesn't need to be throttled, emit immediately 534 DriverDistractionChangeEvent changeEvent = mCurrentDistractionEvent; 535 mClientDispatchHandler.post( 536 () -> dispatchCurrentDistractionEventToClientsLocked(changeEvent)); 537 } 538 } 539 540 @GuardedBy("mLock") scheduleAwarenessDispatchLocked(long uptimeMillis)541 private void scheduleAwarenessDispatchLocked(long uptimeMillis) { 542 if (mIsDispatchQueued) { 543 logd("Dispatch event is throttled and already scheduled."); 544 return; 545 } 546 547 // schedule a dispatch for when throttle window has passed 548 long delayMs = mLastDispatchUptimeMillis + DISPATCH_THROTTLE_MS - uptimeMillis; 549 if (delayMs < 0) { 550 Log.e(TAG, String.format( 551 "Delay for (%s) calculated to be negative (%s), so dispatching immediately", 552 mCurrentDistractionEvent, delayMs)); 553 delayMs = 0; 554 } 555 logd(String.format("Dispatch event (%s) is throttled. Scheduled to emit in %sms", 556 mCurrentDistractionEvent, delayMs)); 557 mIsDispatchQueued = true; 558 mClientDispatchHandler.postDelayed(mDispatchCurrentDistractionRunnable, delayMs); 559 } 560 561 @GuardedBy("mLock") shouldThrottleDispatchEventLocked(long uptimeMillis)562 private boolean shouldThrottleDispatchEventLocked(long uptimeMillis) { 563 return uptimeMillis < mLastDispatchUptimeMillis + DISPATCH_THROTTLE_MS; 564 } 565 566 @GuardedBy("mLock") dispatchCurrentDistractionEventToClientsLocked( DriverDistractionChangeEvent changeEvent)567 private void dispatchCurrentDistractionEventToClientsLocked( 568 DriverDistractionChangeEvent changeEvent) { 569 mLastDispatchUptimeMillis = mTimeSource.uptimeMillis(); 570 logd("Dispatching event to clients: " + changeEvent); 571 int numClients = mDistractionClients.beginBroadcast(); 572 for (int i = 0; i < numClients; i++) { 573 IDriverDistractionChangeListener callback = mDistractionClients.getBroadcastItem(i); 574 try { 575 callback.onDriverDistractionChange(changeEvent); 576 } catch (RemoteException ignores) { 577 // ignore 578 } 579 } 580 mDistractionClients.finishBroadcast(); 581 } 582 583 /** 584 * Internally register the supplier with the specified priority. 585 */ addDriverAwarenessSupplier( ComponentName componentName, IDriverAwarenessSupplier awarenessSupplier, int priority)586 private void addDriverAwarenessSupplier( 587 ComponentName componentName, 588 IDriverAwarenessSupplier awarenessSupplier, 589 int priority) { 590 synchronized (mLock) { 591 mSupplierBinders.put(componentName, awarenessSupplier); 592 mDriverAwarenessSupplierPriorities.put(awarenessSupplier, priority); 593 mPrioritizedDriverAwarenessSuppliers.add(awarenessSupplier); 594 mPrioritizedDriverAwarenessSuppliers.sort(mPrioritizedSuppliersComparator); 595 } 596 } 597 598 /** 599 * Remove references to a supplier. 600 */ removeDriverAwarenessSupplier(ComponentName componentName)601 private void removeDriverAwarenessSupplier(ComponentName componentName) { 602 synchronized (mLock) { 603 IDriverAwarenessSupplier supplier = mSupplierBinders.get(componentName); 604 mSupplierBinders.remove(componentName); 605 mDriverAwarenessSupplierPriorities.remove(supplier); 606 mPrioritizedDriverAwarenessSuppliers.remove(supplier); 607 } 608 } 609 610 /** 611 * Update {@link #mCurrentDriverAwareness} based on the current driver awareness events for each 612 * supplier. 613 */ 614 @GuardedBy("mLock") updateCurrentAwarenessValueLocked()615 private void updateCurrentAwarenessValueLocked() { 616 for (IDriverAwarenessSupplier supplier : mPrioritizedDriverAwarenessSuppliers) { 617 long supplierMaxStaleness = mSupplierConfigs.get(supplier).getMaxStalenessMillis(); 618 DriverAwarenessEventWrapper eventForSupplier = mCurrentAwarenessEventsMap.get(supplier); 619 if (eventForSupplier == null) { 620 continue; 621 } 622 if (supplierMaxStaleness == DriverAwarenessSupplierService.NO_STALENESS) { 623 // this supplier can't be stale, so use its information 624 mCurrentDriverAwareness = eventForSupplier; 625 return; 626 } 627 628 long oldestFreshTimestamp = mTimeSource.elapsedRealtime() - supplierMaxStaleness; 629 if (eventForSupplier.mAwarenessEvent.getTimeStamp() > oldestFreshTimestamp) { 630 // value is still fresh, so use it 631 mCurrentDriverAwareness = eventForSupplier; 632 return; 633 } 634 } 635 636 if (mCurrentDriverAwareness == null) { 637 // There must always at least be a fallback supplier with NO_STALENESS configuration. 638 // Since we control this configuration, getting this exception represents a developer 639 // error in initialization. 640 throw new IllegalStateException( 641 "Unable to determine the current driver awareness value"); 642 } 643 } 644 645 /** 646 * Sets a timer to update the refresh the awareness value once the current value has become 647 * stale. 648 */ 649 @GuardedBy("mLock") scheduleExpirationTimerLocked()650 private void scheduleExpirationTimerLocked() { 651 // reschedule the current awareness expiration task 652 mExpiredDriverAwarenessTimer.reset(); 653 long delay = mCurrentDriverAwareness.mAwarenessEvent.getTimeStamp() 654 - mTimeSource.elapsedRealtime() 655 + mCurrentDriverAwareness.mMaxStaleness; 656 if (delay < 0) { 657 // somehow the event is already stale 658 synchronized (mLock) { 659 updateCurrentAwarenessValueLocked(); 660 } 661 return; 662 } 663 mExpiredDriverAwarenessTimer.schedule(new TimerTask() { 664 @Override 665 public void run() { 666 logd("Driver awareness has become stale. Selecting new awareness level."); 667 synchronized (mLock) { 668 updateCurrentAwarenessValueLocked(); 669 updateCurrentDistractionEventLocked(); 670 } 671 } 672 }, delay); 673 674 logd(String.format( 675 "Current awareness value is stale after %sms and is scheduled to expire in %sms", 676 mCurrentDriverAwareness.mMaxStaleness, delay)); 677 } 678 679 /** 680 * Add the state change to the transition log. 681 * 682 * @param oldValue the old value 683 * @param newValue the new value 684 * @param extra name of the value being changed 685 */ 686 @GuardedBy("mLock") addTransitionLogLocked(float oldValue, float newValue, String extra)687 private void addTransitionLogLocked(float oldValue, float newValue, String extra) { 688 if (mTransitionLogs.size() >= MAX_EVENT_LOG_COUNT) { 689 mTransitionLogs.remove(); 690 } 691 692 Utils.TransitionLog tLog = new Utils.TransitionLog(TAG, oldValue, newValue, 693 System.currentTimeMillis(), extra); 694 mTransitionLogs.add(tLog); 695 } 696 logd(String message)697 private static void logd(String message) { 698 if (Log.isLoggable(TAG, Log.DEBUG)) { 699 Log.d(TAG, message); 700 } 701 } 702 703 @Override getLastDistractionEvent()704 public DriverDistractionChangeEvent getLastDistractionEvent() throws RemoteException { 705 IExperimentalCarImpl.assertPermission(mContext, 706 ExperimentalCar.PERMISSION_READ_CAR_DRIVER_DISTRACTION); 707 synchronized (mLock) { 708 return mCurrentDistractionEvent; 709 } 710 } 711 712 @Override addDriverDistractionChangeListener(IDriverDistractionChangeListener listener)713 public void addDriverDistractionChangeListener(IDriverDistractionChangeListener listener) 714 throws RemoteException { 715 IExperimentalCarImpl.assertPermission(mContext, 716 ExperimentalCar.PERMISSION_READ_CAR_DRIVER_DISTRACTION); 717 if (listener == null) { 718 throw new IllegalArgumentException("IDriverDistractionChangeListener is null"); 719 } 720 mDistractionClients.register(listener); 721 722 DriverDistractionChangeEvent changeEvent = mCurrentDistractionEvent; 723 mClientDispatchHandler.post(() -> { 724 try { 725 listener.onDriverDistractionChange(changeEvent); 726 } catch (RemoteException ignores) { 727 // ignore 728 } 729 }); 730 } 731 732 733 @Override removeDriverDistractionChangeListener(IDriverDistractionChangeListener listener)734 public void removeDriverDistractionChangeListener(IDriverDistractionChangeListener listener) 735 throws RemoteException { 736 IExperimentalCarImpl.assertPermission(mContext, 737 ExperimentalCar.PERMISSION_READ_CAR_DRIVER_DISTRACTION); 738 if (listener == null) { 739 Log.e(TAG, "unregisterUxRestrictionsChangeListener(): listener null"); 740 throw new IllegalArgumentException("Listener is null"); 741 } 742 mDistractionClients.unregister(listener); 743 } 744 745 /** 746 * The service connection between this distraction service and a {@link 747 * DriverAwarenessSupplierService}, communicated through {@link IDriverAwarenessSupplier}. 748 */ 749 private class DriverAwarenessServiceConnection implements ServiceConnection { 750 751 final int mPriority; 752 753 /** 754 * Create an instance of {@link DriverAwarenessServiceConnection}. 755 * 756 * @param priority the priority of the {@link DriverAwarenessSupplierService} that this 757 * connection is for 758 */ DriverAwarenessServiceConnection(int priority)759 DriverAwarenessServiceConnection(int priority) { 760 mPriority = priority; 761 } 762 763 @Override onServiceConnected(ComponentName name, IBinder binder)764 public void onServiceConnected(ComponentName name, IBinder binder) { 765 logd("onServiceConnected, name: " + name + ", binder: " + binder); 766 IDriverAwarenessSupplier service = IDriverAwarenessSupplier.Stub.asInterface( 767 binder); 768 addDriverAwarenessSupplier(name, service, mPriority); 769 try { 770 service.setCallback(new DriverAwarenessSupplierCallback(name)); 771 service.onReady(); 772 } catch (RemoteException e) { 773 Log.e(TAG, "Unable to call onReady on supplier", e); 774 } 775 } 776 777 @Override onServiceDisconnected(ComponentName name)778 public void onServiceDisconnected(ComponentName name) { 779 logd("onServiceDisconnected, name: " + name); 780 removeDriverAwarenessSupplier(name); 781 // TODO(b/146471650) rebind to driver awareness suppliers on service disconnect 782 } 783 } 784 785 /** 786 * Driver awareness listener that keeps a references to some attributes of the supplier. 787 */ 788 private class DriverAwarenessSupplierCallback extends IDriverAwarenessSupplierCallback.Stub { 789 790 private final ComponentName mComponentName; 791 792 /** 793 * Construct an instance of {@link DriverAwarenessSupplierCallback}. 794 * 795 * @param componentName the driver awareness supplier for this listener 796 */ DriverAwarenessSupplierCallback(ComponentName componentName)797 DriverAwarenessSupplierCallback(ComponentName componentName) { 798 mComponentName = componentName; 799 } 800 801 @Override onDriverAwarenessUpdated(DriverAwarenessEvent event)802 public void onDriverAwarenessUpdated(DriverAwarenessEvent event) { 803 IDriverAwarenessSupplier supplier; 804 long maxStaleness; 805 synchronized (mLock) { 806 supplier = mSupplierBinders.get(mComponentName); 807 maxStaleness = mSupplierConfigs.get(supplier).getMaxStalenessMillis(); 808 } 809 if (supplier == null) { 810 // this should never happen. Initialization process would not be correct. 811 throw new IllegalStateException( 812 "No supplier registered for component " + mComponentName); 813 } 814 logd(String.format("Driver awareness updated for %s: %s", 815 supplier.getClass().getSimpleName(), event)); 816 handleDriverAwarenessEvent( 817 new DriverAwarenessEventWrapper(event, supplier, maxStaleness)); 818 } 819 820 @Override onConfigLoaded(DriverAwarenessSupplierConfig config)821 public void onConfigLoaded(DriverAwarenessSupplierConfig config) throws RemoteException { 822 synchronized (mLock) { 823 mSupplierConfigs.put(mSupplierBinders.get(mComponentName), config); 824 } 825 } 826 } 827 828 /** 829 * Wrapper for {@link DriverAwarenessEvent} that includes some information from the supplier 830 * that emitted the event. 831 */ 832 @VisibleForTesting 833 static class DriverAwarenessEventWrapper { 834 final DriverAwarenessEvent mAwarenessEvent; 835 final IDriverAwarenessSupplier mSupplier; 836 final long mMaxStaleness; 837 838 /** 839 * Construct an instance of {@link DriverAwarenessEventWrapper}. 840 * 841 * @param awarenessEvent the driver awareness event being wrapped 842 * @param supplier the driver awareness supplier for this listener 843 * @param maxStaleness the max staleness of the supplier that emitted this event (included 844 * to avoid making a binder call) 845 */ DriverAwarenessEventWrapper( DriverAwarenessEvent awarenessEvent, IDriverAwarenessSupplier supplier, long maxStaleness)846 DriverAwarenessEventWrapper( 847 DriverAwarenessEvent awarenessEvent, 848 IDriverAwarenessSupplier supplier, 849 long maxStaleness) { 850 mAwarenessEvent = awarenessEvent; 851 mSupplier = supplier; 852 mMaxStaleness = maxStaleness; 853 } 854 855 @Override toString()856 public String toString() { 857 return String.format( 858 "DriverAwarenessEventWrapper{mAwarenessChangeEvent=%s, mSupplier=%s, " 859 + "mMaxStaleness=%s}", 860 mAwarenessEvent, mSupplier, mMaxStaleness); 861 } 862 } 863 } 864