blob: eadb3153b674a78183a27fd1e13e64106e9edce2 [file] [log] [blame]
Michael Kellerc0f0bdb2019-12-11 14:58:19 -08001/*
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
17package com.android.car;
18
19import android.annotation.NonNull;
20import android.car.Car;
21import android.car.occupantawareness.IOccupantAwarenessEventCallback;
22import android.car.occupantawareness.OccupantAwarenessDetection;
23import android.car.occupantawareness.OccupantAwarenessDetection.VehicleOccupantRole;
24import android.car.occupantawareness.SystemStatusEvent;
25import android.car.occupantawareness.SystemStatusEvent.DetectionTypeFlags;
26import android.content.Context;
27import android.hardware.automotive.occupant_awareness.IOccupantAwareness;
28import android.hardware.automotive.occupant_awareness.IOccupantAwarenessClientCallback;
29import android.os.RemoteCallbackList;
30import android.os.RemoteException;
31import android.os.ServiceManager;
Felipe Leme176a5fd2021-01-20 15:48:33 -080032import android.util.IndentingPrintWriter;
Eric Jeongbd5fb562020-12-21 13:49:40 -080033import android.util.Slog;
Michael Kellerc0f0bdb2019-12-11 14:58:19 -080034
35import com.android.internal.annotations.GuardedBy;
36import com.android.internal.annotations.VisibleForTesting;
37
Michael Kellerc0f0bdb2019-12-11 14:58:19 -080038import java.lang.ref.WeakReference;
39
40/**
41 * A service that listens to an Occupant Awareness Detection system across a HAL boundary and
42 * exposes the data to system clients in Android via a {@link
43 * android.car.occupantawareness.OccupantAwarenessManager}.
44 *
45 * <p>The service exposes the following detections types:
46 *
47 * <h1>Presence Detection</h1>
48 *
49 * Detects whether a person is present for each seat location.
50 *
51 * <h1>Gaze Detection</h1>
52 *
53 * Detects where an occupant is looking and for how long they have been looking at the specified
54 * target.
55 *
56 * <h1>Driver Monitoring</h1>
57 *
58 * Detects whether a driver is looking on or off-road and for how long they have been looking there.
59 */
60public class OccupantAwarenessService
61 extends android.car.occupantawareness.IOccupantAwarenessManager.Stub
62 implements CarServiceBase {
Mayank Garg72c71d22021-02-03 23:54:45 -080063 private static final String TAG = CarLog.tagFor(OccupantAwarenessService.class);
Michael Kellerac2ed202020-01-30 14:11:16 -080064 private static final boolean DBG = false;
65
66 // HAL service identifier name.
Michael Kellerd3990112020-02-03 10:41:06 -080067 @VisibleForTesting
68 static final String OAS_SERVICE_ID =
Michael Kellerc0f0bdb2019-12-11 14:58:19 -080069 "android.hardware.automotive.occupant_awareness.IOccupantAwareness/default";
70
71 private final Object mLock = new Object();
72 private final Context mContext;
73
74 @GuardedBy("mLock")
75 private IOccupantAwareness mOasHal;
76
77 private final ChangeListenerToHalService mHalListener = new ChangeListenerToHalService(this);
78
79 private class ChangeCallbackList extends RemoteCallbackList<IOccupantAwarenessEventCallback> {
80 private final WeakReference<OccupantAwarenessService> mOasService;
81
82 ChangeCallbackList(OccupantAwarenessService oasService) {
83 mOasService = new WeakReference<>(oasService);
84 }
85
86 /** Handle callback death. */
87 @Override
88 public void onCallbackDied(IOccupantAwarenessEventCallback listener) {
Eric Jeongbd5fb562020-12-21 13:49:40 -080089 Slog.i(TAG, "binderDied: " + listener.asBinder());
Michael Kellerc0f0bdb2019-12-11 14:58:19 -080090
91 OccupantAwarenessService service = mOasService.get();
92 if (service != null) {
93 service.handleClientDisconnected();
94 }
95 }
96 }
97
98 @GuardedBy("mLock")
99 private final ChangeCallbackList mListeners = new ChangeCallbackList(this);
100
101 /** Creates an OccupantAwarenessService instance given a {@link Context}. */
102 public OccupantAwarenessService(Context context) {
103 mContext = context;
104 }
105
106 /** Creates an OccupantAwarenessService instance given a {@link Context}. */
107 @VisibleForTesting
108 OccupantAwarenessService(Context context, IOccupantAwareness oasInterface) {
109 mContext = context;
110 mOasHal = oasInterface;
111 }
112
113 @Override
114 public void init() {
Michael Keller16ac9302020-02-14 10:44:51 -0800115 logd("Initializing service");
Michael Kellerd3990112020-02-03 10:41:06 -0800116 connectToHalServiceIfNotConnected();
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800117 }
118
119 @Override
120 public void release() {
Michael Kellerd3990112020-02-03 10:41:06 -0800121 logd("Will stop detection and disconnect listeners");
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800122 stopDetectionGraph();
123 mListeners.kill();
124 }
125
126 @Override
Felipe Leme176a5fd2021-01-20 15:48:33 -0800127 public void dump(IndentingPrintWriter writer) {
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800128 writer.println("*OccupantAwarenessService*");
129 writer.println(
130 String.format(
Michael Kellerd3990112020-02-03 10:41:06 -0800131 "%s to HAL service", mOasHal == null ? "NOT connected" : "Connected"));
132 writer.println(
133 String.format(
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800134 "%d change listeners subscribed.",
135 mListeners.getRegisteredCallbackCount()));
136 }
137
Michael Kellerd3990112020-02-03 10:41:06 -0800138 /** Attempts to connect to the HAL service if it is not already connected. */
139 private void connectToHalServiceIfNotConnected() {
140 logd("connectToHalServiceIfNotConnected()");
141
142 synchronized (mLock) {
143 // If already connected, nothing more needs to be done.
144 if (mOasHal != null) {
145 logd("Client is already connected, nothing more to do");
146 return;
147 }
148
149 // Attempt to find the HAL service.
150 logd("Attempting to connect to client at: " + OAS_SERVICE_ID);
151 mOasHal =
152 android.hardware.automotive.occupant_awareness.IOccupantAwareness.Stub
153 .asInterface(ServiceManager.getService(OAS_SERVICE_ID));
154
155 if (mOasHal == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800156 Slog.e(TAG, "Failed to find OAS hal_service at: [" + OAS_SERVICE_ID + "]");
Michael Kellerd3990112020-02-03 10:41:06 -0800157 return;
158 }
159
160 // Register for callbacks.
161 try {
162 mOasHal.setCallback(mHalListener);
163 } catch (RemoteException e) {
164 mOasHal = null;
Eric Jeongbd5fb562020-12-21 13:49:40 -0800165 Slog.e(TAG, "Failed to set callback: " + e);
Michael Kellerd3990112020-02-03 10:41:06 -0800166 return;
167 }
168
169 logd("Successfully connected to hal_service at: [" + OAS_SERVICE_ID + "]");
170 }
171 }
172
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800173 /** Sends a message via the HAL to start the detection graph. */
174 private void startDetectionGraph() {
Michael Kellerd3990112020-02-03 10:41:06 -0800175 logd("Attempting to start detection graph");
Michael Kellerac2ed202020-01-30 14:11:16 -0800176
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800177 // Grab a copy of 'mOasHal' to avoid sitting on the lock longer than is necessary.
178 IOccupantAwareness hal;
179 synchronized (mLock) {
180 hal = mOasHal;
181 }
182
183 if (hal != null) {
184 try {
185 hal.startDetection();
186 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800187 Slog.e(TAG, "startDetection() HAL invocation failed: " + e, e);
Michael Kellerd3990112020-02-03 10:41:06 -0800188
189 synchronized (mLock) {
190 mOasHal = null;
191 }
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800192 }
Michael Kellerac2ed202020-01-30 14:11:16 -0800193 } else {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800194 Slog.e(TAG, "No HAL is connected. Cannot request graph start");
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800195 }
196 }
197
198 /** Sends a message via the HAL to stop the detection graph. */
199 private void stopDetectionGraph() {
Michael Keller16ac9302020-02-14 10:44:51 -0800200 logd("Attempting to stop detection graph.");
201
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800202 // Grab a copy of 'mOasHal' to avoid sitting on the lock longer than is necessary.
203 IOccupantAwareness hal;
204 synchronized (mLock) {
205 hal = mOasHal;
206 }
207
208 if (hal != null) {
209 try {
210 hal.stopDetection();
211 } catch (RemoteException e) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800212 Slog.e(TAG, "stopDetection() HAL invocation failed: " + e, e);
Michael Kellerd3990112020-02-03 10:41:06 -0800213
214 synchronized (mLock) {
215 mOasHal = null;
216 }
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800217 }
Michael Kellerac2ed202020-01-30 14:11:16 -0800218 } else {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800219 Slog.e(TAG, "No HAL is connected. Cannot request graph stop");
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800220 }
221 }
222
223 /**
224 * Gets the vehicle capabilities for a given role.
225 *
226 * <p>Capabilities are static for a given vehicle configuration and need only be queried once
227 * per vehicle. Once capability is determined, clients should query system status to see if the
228 * subsystem is currently ready to serve.
229 *
230 * <p>Requires {@link android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE} read
231 * permissions to access.
232 *
233 * @param role {@link VehicleOccupantRole} to query for.
234 * @return Flags indicating supported capabilities for the role.
235 */
236 public @DetectionTypeFlags int getCapabilityForRole(@VehicleOccupantRole int role) {
237 ICarImpl.assertPermission(mContext, Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE);
238
Michael Kellerd3990112020-02-03 10:41:06 -0800239 connectToHalServiceIfNotConnected();
240
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800241 // Grab a copy of 'mOasHal' to avoid sitting on the lock longer than is necessary.
242 IOccupantAwareness hal;
243 synchronized (mLock) {
244 hal = mOasHal;
245 }
246
247 if (hal != null) {
248 try {
249 return hal.getCapabilityForRole(role);
250 } catch (RemoteException e) {
Michael Kellerd3990112020-02-03 10:41:06 -0800251
Eric Jeongbd5fb562020-12-21 13:49:40 -0800252 Slog.e(TAG, "getCapabilityForRole() HAL invocation failed: " + e, e);
Michael Kellerd3990112020-02-03 10:41:06 -0800253
254 synchronized (mLock) {
255 mOasHal = null;
256 }
257
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800258 return SystemStatusEvent.DETECTION_TYPE_NONE;
259 }
260 } else {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800261 Slog.e(
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800262 TAG,
263 "getCapabilityForRole(): No HAL interface has been provided. Cannot get"
264 + " capabilities");
265 return SystemStatusEvent.DETECTION_TYPE_NONE;
266 }
267 }
268
269 /**
270 * Registers a {@link IOccupantAwarenessEventCallback} to be notified for changes in the system
271 * state.
272 *
273 * <p>Requires {@link android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE} read
274 * permissions to access.
275 *
276 * @param listener {@link IOccupantAwarenessEventCallback} listener to register.
277 */
278 @Override
279 public void registerEventListener(@NonNull IOccupantAwarenessEventCallback listener) {
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800280 ICarImpl.assertPermission(mContext, Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE);
281
Michael Kellerd3990112020-02-03 10:41:06 -0800282 connectToHalServiceIfNotConnected();
283
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800284 synchronized (mLock) {
Michael Kellerd3990112020-02-03 10:41:06 -0800285 if (mOasHal == null) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800286 Slog.e(TAG, "Attempting to register a listener, but could not connect to HAL.");
Michael Kellerd3990112020-02-03 10:41:06 -0800287 return;
288 }
289
290 logd("Registering a new listener");
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800291 mListeners.register(listener);
292
293 // After the first client connects, request that the detection graph start.
294 if (mListeners.getRegisteredCallbackCount() == 1) {
295 startDetectionGraph();
296 }
297 }
298 }
299
300 /**
301 * Unregister the given {@link IOccupantAwarenessEventCallback} listener from receiving events.
302 *
303 * <p>Requires {@link android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE} read
304 * permissions to access.
305 *
306 * @param listener {@link IOccupantAwarenessEventCallback} client to unregister.
307 */
308 @Override
309 public void unregisterEventListener(@NonNull IOccupantAwarenessEventCallback listener) {
310 ICarImpl.assertPermission(mContext, Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE);
311
Michael Kellerd3990112020-02-03 10:41:06 -0800312 connectToHalServiceIfNotConnected();
313
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800314 synchronized (mLock) {
315 mListeners.unregister(listener);
316 }
317
318 // When the last client disconnects, request that the detection graph stop.
319 handleClientDisconnected();
320 }
321
322 /** Processes a detection event and propagates it to registered clients. */
323 @VisibleForTesting
324 void processStatusEvent(@NonNull SystemStatusEvent statusEvent) {
325 int idx = mListeners.beginBroadcast();
326 while (idx-- > 0) {
327 IOccupantAwarenessEventCallback listener = mListeners.getBroadcastItem(idx);
328 try {
329 listener.onStatusChanged(statusEvent);
330 } catch (RemoteException e) {
331 // It's likely the connection snapped. Let binder death handle the situation.
Eric Jeongbd5fb562020-12-21 13:49:40 -0800332 Slog.e(TAG, "onStatusChanged() invocation failed: " + e, e);
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800333 }
334 }
335 mListeners.finishBroadcast();
336 }
337
338 /** Processes a detection event and propagates it to registered clients. */
339 @VisibleForTesting
340 void processDetectionEvent(@NonNull OccupantAwarenessDetection detection) {
341 int idx = mListeners.beginBroadcast();
342 while (idx-- > 0) {
343 IOccupantAwarenessEventCallback listener = mListeners.getBroadcastItem(idx);
344 try {
345 listener.onDetectionEvent(detection);
346 } catch (RemoteException e) {
347 // It's likely the connection snapped. Let binder death handle the situation.
Eric Jeongbd5fb562020-12-21 13:49:40 -0800348 Slog.e(TAG, "onDetectionEvent() invocation failed: " + e, e);
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800349 }
350 }
351 mListeners.finishBroadcast();
352 }
353
354 /** Handle client disconnections, possibly stopping the detection graph. */
355 void handleClientDisconnected() {
356 // If the last client disconnects, requests that the graph stops.
357 synchronized (mLock) {
358 if (mListeners.getRegisteredCallbackCount() == 0) {
359 stopDetectionGraph();
360 }
361 }
362 }
363
Michael Kellerd3990112020-02-03 10:41:06 -0800364 private static void logd(String msg) {
365 if (DBG) {
Eric Jeongbd5fb562020-12-21 13:49:40 -0800366 Slog.d(TAG, msg);
Michael Kellerd3990112020-02-03 10:41:06 -0800367 }
368 }
369
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800370 /**
371 * Class that implements the listener interface and gets called back from the {@link
372 * android.hardware.automotive.occupant_awareness.IOccupantAwarenessClientCallback} across the
373 * binder interface.
374 */
375 private static class ChangeListenerToHalService extends IOccupantAwarenessClientCallback.Stub {
376 private final WeakReference<OccupantAwarenessService> mOasService;
377
378 ChangeListenerToHalService(OccupantAwarenessService oasService) {
379 mOasService = new WeakReference<>(oasService);
380 }
381
382 @Override
383 public void onSystemStatusChanged(int inputDetectionFlags, byte inputStatus) {
384 OccupantAwarenessService service = mOasService.get();
385 if (service != null) {
386 service.processStatusEvent(
387 OccupantAwarenessUtils.convertToStatusEvent(
388 inputDetectionFlags, inputStatus));
389 }
390 }
391
392 @Override
393 public void onDetectionEvent(
394 android.hardware.automotive.occupant_awareness.OccupantDetections detections) {
395 OccupantAwarenessService service = mOasService.get();
396 if (service != null) {
397 for (android.hardware.automotive.occupant_awareness.OccupantDetection detection :
398 detections.detections) {
399 service.processDetectionEvent(
400 OccupantAwarenessUtils.convertToDetectionEvent(
401 detections.timeStampMillis, detection));
402 }
403 }
404 }
Jeongik Cha1d04bdd2020-04-08 22:19:52 +0900405
406 @Override
407 public int getInterfaceVersion() {
408 return this.VERSION;
409 }
410
411 @Override
412 public String getInterfaceHash() {
413 return this.HASH;
414 }
Michael Kellerc0f0bdb2019-12-11 14:58:19 -0800415 }
416}