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