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 android.car.occupantawareness; 18 19 import android.annotation.NonNull; 20 import android.annotation.RequiresPermission; 21 import android.car.Car; 22 import android.car.CarManagerBase; 23 import android.car.annotation.RequiredFeature; 24 import android.car.occupantawareness.OccupantAwarenessDetection.VehicleOccupantRole; 25 import android.car.occupantawareness.SystemStatusEvent.DetectionTypeFlags; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.RemoteException; 31 import android.util.Slog; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.lang.ref.WeakReference; 36 37 /** 38 * API exposing Occupant Awareness System data. 39 * 40 * <p>Supported detections include: presence detection, {@link GazeDetection} and {@link 41 * DriverMonitoringDetection}. 42 * 43 * @hide 44 */ 45 @RequiredFeature(Car.OCCUPANT_AWARENESS_SERVICE) 46 public class OccupantAwarenessManager extends CarManagerBase { 47 private static final String TAG = "OAS.Manager"; 48 private static final boolean DBG = false; 49 50 private static final int MSG_HANDLE_SYSTEM_STATUS_CHANGE = 0; 51 private static final int MSG_HANDLE_DETECTION_EVENT = 1; 52 53 private final android.car.occupantawareness.IOccupantAwarenessManager mOccupantAwarenessService; 54 55 private final Object mLock = new Object(); 56 57 @GuardedBy("mLock") 58 private ChangeCallback mChangeCallback; 59 60 private final EventCallbackHandler mEventCallbackHandler; 61 62 @GuardedBy("mLock") 63 private ChangeListenerToService mListenerToService; 64 65 /** @hide */ OccupantAwarenessManager(Car car, IBinder service)66 public OccupantAwarenessManager(Car car, IBinder service) { 67 super(car); 68 mOccupantAwarenessService = 69 android.car.occupantawareness.IOccupantAwarenessManager.Stub.asInterface(service); 70 71 mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper()); 72 } 73 74 /** @hide */ 75 @Override onCarDisconnected()76 public void onCarDisconnected() { 77 synchronized (mLock) { 78 mChangeCallback = null; 79 mListenerToService = null; 80 } 81 } 82 83 /** 84 * Gets the detection capabilities for a given {@link VehicleOccupantRole} in this vehicle. 85 * 86 * <p>There may be different detection capabilities for different {@link VehicleOccupantRole}. 87 * Each role should be queried independently. 88 * 89 * <p>Capabilities are static for a given vehicle configuration and need only be queried once 90 * per vehicle. Once capability is determined, clients should query system status via {@link 91 * SystemStatusEvent} to see if the subsystem is currently online and ready to serve. 92 * 93 * @param role {@link VehicleOccupantRole} to query for. 94 * @return {@link SystemStatusEvent.DetectionTypeFlags} indicating supported capabilities for 95 * the role. 96 */ 97 @RequiresPermission(value = Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE) getCapabilityForRole(@ehicleOccupantRole int role)98 public @DetectionTypeFlags int getCapabilityForRole(@VehicleOccupantRole int role) { 99 try { 100 return mOccupantAwarenessService.getCapabilityForRole(role); 101 } catch (RemoteException e) { 102 return handleRemoteExceptionFromCarService(e, 0); 103 } 104 } 105 106 /** 107 * Callbacks for listening to changes to {@link SystemStatusEvent} and {@link 108 * OccupantAwarenessDetection}. 109 */ 110 public abstract static class ChangeCallback { 111 /** 112 * Called when the system state changes changes. 113 * 114 * @param systemStatus The new system state as a {@link SystemStatusEvent}. 115 */ onSystemStateChanged(@onNull SystemStatusEvent systemStatus)116 public abstract void onSystemStateChanged(@NonNull SystemStatusEvent systemStatus); 117 118 /** 119 * Called when a detection event is generated. 120 * 121 * @param event The new detection state as a {@link OccupantAwarenessDetection}. 122 */ onDetectionEvent(@onNull OccupantAwarenessDetection event)123 public abstract void onDetectionEvent(@NonNull OccupantAwarenessDetection event); 124 } 125 126 /** 127 * Registers a {@link ChangeCallback} for listening for events. 128 * 129 * <p>If a listener has already been registered, it has to be unregistered before registering 130 * the new one. 131 * 132 * @param callback {@link ChangeCallback} to register. 133 * @throws IllegalStateException if an existing callback is already registered. 134 */ 135 @RequiresPermission(value = Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE) registerChangeCallback(@onNull ChangeCallback callback)136 public void registerChangeCallback(@NonNull ChangeCallback callback) { 137 if (DBG) { 138 Slog.d(TAG, "Registering change listener"); 139 } 140 141 synchronized (mLock) { 142 // Check if the listener has been already registered. 143 if (mChangeCallback != null) { 144 throw new IllegalStateException( 145 "Attempting to register a new listener when an existing listener has" 146 + " already been registered."); 147 } 148 149 mChangeCallback = callback; 150 151 try { 152 if (mListenerToService == null) { 153 mListenerToService = new ChangeListenerToService(this); 154 } 155 156 mOccupantAwarenessService.registerEventListener(mListenerToService); 157 } catch (RemoteException e) { 158 handleRemoteExceptionFromCarService(e); 159 } 160 } 161 } 162 163 /** Unregisters a previously registered {@link ChangeCallback}. */ 164 @RequiresPermission(value = Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE) unregisterChangeCallback()165 public void unregisterChangeCallback() { 166 if (DBG) { 167 Slog.d(TAG, "Unregistering change listener"); 168 } 169 170 synchronized (mLock) { 171 if (mChangeCallback == null) { 172 Slog.e(TAG, "No listener exists to unregister."); 173 return; 174 } 175 mChangeCallback = null; 176 } 177 178 synchronized (mLock) { 179 try { 180 mOccupantAwarenessService.unregisterEventListener(mListenerToService); 181 } catch (RemoteException e) { 182 handleRemoteExceptionFromCarService(e); 183 } 184 185 mListenerToService = null; 186 } 187 } 188 189 /** 190 * Class that implements the listener interface and gets called back from the {@link 191 * com.android.car.IOccupantAwarenessEventCallback} across the binder interface. 192 */ 193 private static class ChangeListenerToService extends IOccupantAwarenessEventCallback.Stub { 194 private final WeakReference<OccupantAwarenessManager> mOccupantAwarenessManager; 195 ChangeListenerToService(OccupantAwarenessManager manager)196 ChangeListenerToService(OccupantAwarenessManager manager) { 197 mOccupantAwarenessManager = new WeakReference<>(manager); 198 } 199 200 @Override onStatusChanged(SystemStatusEvent systemStatus)201 public void onStatusChanged(SystemStatusEvent systemStatus) { 202 OccupantAwarenessManager manager = mOccupantAwarenessManager.get(); 203 if (manager != null) { 204 manager.handleSystemStatusChanged(systemStatus); 205 } 206 } 207 208 @Override onDetectionEvent(OccupantAwarenessDetection event)209 public void onDetectionEvent(OccupantAwarenessDetection event) { 210 OccupantAwarenessManager manager = mOccupantAwarenessManager.get(); 211 if (manager != null) { 212 manager.handleDetectionEvent(event); 213 } 214 } 215 } 216 217 /** 218 * Gets the {@link SystemStatusEvent} from the service listener {@link 219 * SystemStateChangeListenerToService} and dispatches it to a handler provided to the manager. 220 * 221 * @param systemStatus {@link SystemStatusEvent} that has been registered to listen on 222 */ handleSystemStatusChanged(SystemStatusEvent systemStatus)223 private void handleSystemStatusChanged(SystemStatusEvent systemStatus) { 224 // Send a message via the handler. 225 mEventCallbackHandler.sendMessage( 226 mEventCallbackHandler.obtainMessage(MSG_HANDLE_SYSTEM_STATUS_CHANGE, systemStatus)); 227 } 228 229 /** 230 * Checks for the listeners to list of {@link SystemStatusEvent} and calls them back in the 231 * callback handler thread. 232 * 233 * @param systemStatus {@link SystemStatusEvent} 234 */ dispatchSystemStatusToClient(SystemStatusEvent systemStatus)235 private void dispatchSystemStatusToClient(SystemStatusEvent systemStatus) { 236 if (systemStatus == null) { 237 return; 238 } 239 240 synchronized (mLock) { 241 if (mChangeCallback != null) { 242 mChangeCallback.onSystemStateChanged(systemStatus); 243 } 244 } 245 } 246 247 /** 248 * Gets the {@link OccupantAwarenessDetection} from the service listener {@link 249 * DetectionEventListenerToService} and dispatches it to a handler provided to the manager. 250 * 251 * @param detectionEvent {@link OccupantAwarenessDetection} that has been registered to listen 252 * on 253 */ handleDetectionEvent(OccupantAwarenessDetection detectionEvent)254 private void handleDetectionEvent(OccupantAwarenessDetection detectionEvent) { 255 // Send a message via the handler. 256 mEventCallbackHandler.sendMessage( 257 mEventCallbackHandler.obtainMessage(MSG_HANDLE_DETECTION_EVENT, detectionEvent)); 258 } 259 260 /** 261 * Checks for the listeners to list of {@link 262 * android.car.occupantawareness.OccupantAwarenessDetection} and calls them back in the callback 263 * handler thread. 264 * 265 * @param detectionEvent {@link OccupantAwarenessDetection} 266 */ dispatchDetectionEventToClient(OccupantAwarenessDetection detectionEvent)267 private void dispatchDetectionEventToClient(OccupantAwarenessDetection detectionEvent) { 268 if (detectionEvent == null) { 269 return; 270 } 271 272 ChangeCallback callback; 273 274 synchronized (mLock) { 275 callback = mChangeCallback; 276 } 277 278 if (callback != null) { 279 280 callback.onDetectionEvent(detectionEvent); 281 } 282 } 283 284 /** Callback handler to dispatch system status changes to the corresponding listeners. */ 285 private static final class EventCallbackHandler extends Handler { 286 private final WeakReference<OccupantAwarenessManager> mOccupantAwarenessManager; 287 EventCallbackHandler(OccupantAwarenessManager manager, Looper looper)288 EventCallbackHandler(OccupantAwarenessManager manager, Looper looper) { 289 super(looper); 290 mOccupantAwarenessManager = new WeakReference<>(manager); 291 } 292 293 @Override handleMessage(Message msg)294 public void handleMessage(Message msg) { 295 OccupantAwarenessManager mgr = mOccupantAwarenessManager.get(); 296 if (mgr != null) { 297 298 switch (msg.what) { 299 case MSG_HANDLE_SYSTEM_STATUS_CHANGE: 300 mgr.dispatchSystemStatusToClient((SystemStatusEvent) msg.obj); 301 break; 302 303 case MSG_HANDLE_DETECTION_EVENT: 304 mgr.dispatchDetectionEventToClient((OccupantAwarenessDetection) msg.obj); 305 break; 306 307 default: 308 throw new RuntimeException("Unknown message " + msg.what); 309 } 310 } 311 } 312 } 313 } 314