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