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