1 /* 2 * Copyright (C) 2020 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.NonNull; 20 import android.annotation.Nullable; 21 import android.car.Car; 22 import android.car.experimental.DriverAwarenessEvent; 23 import android.car.experimental.DriverAwarenessSupplierService; 24 import android.car.occupantawareness.OccupantAwarenessDetection; 25 import android.car.occupantawareness.OccupantAwarenessManager; 26 import android.car.occupantawareness.SystemStatusEvent; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.res.Resources; 30 import android.os.IBinder; 31 import android.util.Log; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.internal.annotations.VisibleForTesting; 35 36 /** 37 * A Driver Awareness Supplier that estimates the driver's current level of awareness based on gaze 38 * history. 39 * 40 * <p>Attention is facilitated via {@link OccupantAwarenessManager}, which is an optional component 41 * and may not be available on every platform. 42 */ 43 public class GazeDriverAwarenessSupplier extends DriverAwarenessSupplierService { 44 private static final String TAG = "Car.OAS.GazeAwarenessSupplier"; 45 46 /* Maximum allowable staleness before gaze data should be considered unreliable for attention 47 * monitoring, in milliseconds. */ 48 @VisibleForTesting 49 static final long MAX_STALENESS_MILLIS = 500; 50 51 private final Object mLock = new Object(); 52 private final ITimeSource mTimeSource; 53 54 @GuardedBy("mLock") 55 private GazeAttentionProcessor.Configuration mConfiguration; 56 57 @Nullable 58 private Context mContext; 59 60 @GuardedBy("mLock") 61 private Car mCar; 62 63 @GuardedBy("mLock") 64 private OccupantAwarenessManager mOasManager; 65 66 @GuardedBy("mLock") 67 private GazeAttentionProcessor mProcessor; 68 69 /** 70 * Empty constructor allows system service creation. 71 */ GazeDriverAwarenessSupplier()72 public GazeDriverAwarenessSupplier() { 73 this(/* context= */ null, new SystemTimeSource()); 74 } 75 76 @VisibleForTesting GazeDriverAwarenessSupplier(Context context, ITimeSource timeSource)77 GazeDriverAwarenessSupplier(Context context, ITimeSource timeSource) { 78 mContext = context; 79 mTimeSource = timeSource; 80 } 81 82 @Override onCreate()83 public void onCreate() { 84 super.onCreate(); 85 if (mContext == null) { 86 mContext = this; 87 } 88 } 89 90 @Override onDestroy()91 public void onDestroy() { 92 synchronized (mLock) { 93 if (mCar != null && mCar.isConnected()) { 94 mCar.disconnect(); 95 } 96 } 97 super.onDestroy(); 98 } 99 100 /** 101 * Gets the self-reported maximum allowable staleness before the supplier should be considered 102 * failed, in milliseconds. 103 */ 104 @Override getMaxStalenessMillis()105 public long getMaxStalenessMillis() { 106 return MAX_STALENESS_MILLIS; 107 } 108 109 @Override onBind(Intent intent)110 public IBinder onBind(Intent intent) { 111 logd("onBind with intent: " + intent); 112 return super.onBind(intent); 113 } 114 115 @Override onReady()116 public void onReady() { 117 synchronized (mLock) { 118 mConfiguration = loadConfiguration(); 119 mProcessor = new GazeAttentionProcessor(mConfiguration); 120 mCar = Car.createCar(mContext); 121 if (mCar != null) { 122 if (mOasManager == null && mCar.isFeatureEnabled(Car.OCCUPANT_AWARENESS_SERVICE)) { 123 mOasManager = 124 (OccupantAwarenessManager) 125 mCar.getCarManager(Car.OCCUPANT_AWARENESS_SERVICE); 126 127 if (mOasManager == null) { 128 Log.e(TAG, "Failed to get OccupantAwarenessManager."); 129 } 130 } 131 132 if (mOasManager != null) { 133 logd("Registering callback with OAS manager"); 134 mOasManager.registerChangeCallback(new ChangeCallback()); 135 } 136 } 137 } 138 139 // Send an initial value once the provider is ready, as required by {link 140 // IDriverAwarenessSupplierCallback}. 141 emitAwarenessEvent( 142 new DriverAwarenessEvent( 143 mTimeSource.elapsedRealtime(), mConfiguration.initialValue)); 144 } 145 146 /** 147 * Returns whether this car supports gaze based awareness. 148 * 149 * <p>The underlying Occupant Awareness System is optional and may not be supported on every 150 * vehicle. This method must be called *after* onReady(). 151 */ supportsGaze()152 public boolean supportsGaze() throws IllegalStateException { 153 synchronized (mLock) { 154 if (mCar == null) { 155 throw new IllegalStateException( 156 "Must call onReady() before querying for gaze support"); 157 } 158 159 // Occupant Awareness is optional and may not be available on this machine. If not 160 // available, the returned manager will be null. 161 if (mOasManager == null) { 162 logd("Occupant Awareness System is not available on this machine"); 163 return false; 164 } 165 166 int driver_capability = 167 mOasManager.getCapabilityForRole( 168 OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER); 169 170 logd("Driver capabilities flags: " + driver_capability); 171 172 return (driver_capability & SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING) > 0; 173 } 174 } 175 176 /** Builds {@link GazeAttentionProcessor.Configuration} using context resources. */ loadConfiguration()177 private GazeAttentionProcessor.Configuration loadConfiguration() { 178 Resources resources = mContext.getResources(); 179 180 return new GazeAttentionProcessor.Configuration( 181 resources.getFloat(R.fraction.driverAwarenessGazeModelInitialValue), 182 resources.getFloat(R.fraction.driverAwarenessGazeModelDecayRate), 183 resources.getFloat(R.fraction.driverAwarenessGazeModelGrowthRate)); 184 } 185 186 /** Processes a new detection event, updating the current attention value. */ 187 @VisibleForTesting processDetectionEvent(@onNull OccupantAwarenessDetection event)188 void processDetectionEvent(@NonNull OccupantAwarenessDetection event) { 189 if (event.role == OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER 190 && event.gazeDetection != null) { 191 float awarenessValue; 192 synchronized (mLock) { 193 awarenessValue = 194 mProcessor.updateAttention(event.gazeDetection, event.timestampMillis); 195 } 196 197 emitAwarenessEvent(new DriverAwarenessEvent(event.timestampMillis, awarenessValue)); 198 } 199 } 200 201 @VisibleForTesting emitAwarenessEvent(@onNull DriverAwarenessEvent event)202 void emitAwarenessEvent(@NonNull DriverAwarenessEvent event) { 203 logd("Emitting new event: " + event); 204 onDriverAwarenessUpdated(event); 205 } 206 logd(String message)207 private static void logd(String message) { 208 if (Log.isLoggable(TAG, Log.DEBUG)) { 209 Log.d(TAG, message); 210 } 211 } 212 213 /** Callback when the system status changes or a new detection is made. */ 214 private class ChangeCallback extends OccupantAwarenessManager.ChangeCallback { 215 /** Called when the system state changes changes. */ 216 @Override onSystemStateChanged(@onNull SystemStatusEvent status)217 public void onSystemStateChanged(@NonNull SystemStatusEvent status) { 218 logd("New status: " + status); 219 } 220 221 /** Called when a detection event is generated. */ 222 @Override onDetectionEvent(@onNull OccupantAwarenessDetection event)223 public void onDetectionEvent(@NonNull OccupantAwarenessDetection event) { 224 logd("New detection: " + event); 225 processDetectionEvent(event); 226 } 227 } 228 } 229