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