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 package com.android.car; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import android.car.occupantawareness.IOccupantAwarenessEventCallback; 21 import android.car.occupantawareness.OccupantAwarenessDetection; 22 import android.car.occupantawareness.SystemStatusEvent; 23 import android.content.Context; 24 import android.hardware.automotive.occupant_awareness.IOccupantAwareness; 25 import android.hardware.automotive.occupant_awareness.IOccupantAwarenessClientCallback; 26 import android.hardware.automotive.occupant_awareness.OccupantAwarenessStatus; 27 import android.hardware.automotive.occupant_awareness.OccupantDetections; 28 import android.hardware.automotive.occupant_awareness.Role; 29 import android.os.RemoteException; 30 31 import androidx.test.core.app.ApplicationProvider; 32 import androidx.test.filters.MediumTest; 33 import androidx.test.runner.AndroidJUnit4; 34 35 import org.junit.After; 36 import org.junit.Before; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 @RunWith(AndroidJUnit4.class) 44 @MediumTest 45 public final class OccupantAwarenessServiceTest { 46 /** 47 * Mock implementation of {@link 48 * android.hardware.automotive.occupant_awareness.IOccupantAwareness} for testing the service 49 * and manager. 50 */ 51 private static final class MockOasHal 52 extends android.hardware.automotive.occupant_awareness.IOccupantAwareness.Stub { 53 private IOccupantAwarenessClientCallback mCallback; 54 private boolean mGraphIsRunning; 55 MockOasHal()56 MockOasHal() {} 57 58 /** Returns whether the mock graph is running. */ isGraphRunning()59 public boolean isGraphRunning() { 60 return mGraphIsRunning; 61 } 62 63 @Override getLatestDetection(OccupantDetections detections)64 public void getLatestDetection(OccupantDetections detections) {} 65 66 @Override setCallback(IOccupantAwarenessClientCallback callback)67 public void setCallback(IOccupantAwarenessClientCallback callback) { 68 mCallback = callback; 69 } 70 71 @Override getState(int occupantRole, int detectionCapability)72 public @OccupantAwarenessStatus byte getState(int occupantRole, int detectionCapability) { 73 return OccupantAwarenessStatus.READY; 74 } 75 76 @Override startDetection()77 public @OccupantAwarenessStatus byte startDetection() { 78 mGraphIsRunning = true; 79 return OccupantAwarenessStatus.READY; 80 } 81 82 @Override stopDetection()83 public @OccupantAwarenessStatus byte stopDetection() { 84 mGraphIsRunning = false; 85 return OccupantAwarenessStatus.READY; 86 } 87 88 @Override getCapabilityForRole(@ole int occupantRole)89 public int getCapabilityForRole(@Role int occupantRole) { 90 if (occupantRole == OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER) { 91 return SystemStatusEvent.DETECTION_TYPE_PRESENCE 92 | SystemStatusEvent.DETECTION_TYPE_GAZE 93 | SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING; 94 } else if (occupantRole 95 == OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER) { 96 return SystemStatusEvent.DETECTION_TYPE_PRESENCE; 97 } else { 98 return SystemStatusEvent.DETECTION_TYPE_NONE; 99 } 100 } 101 102 /** Causes a status event to be generated with the specified flags. */ fireStatusEvent(int detectionFlags, @OccupantAwarenessStatus byte status)103 public void fireStatusEvent(int detectionFlags, @OccupantAwarenessStatus byte status) 104 throws RemoteException { 105 if (mCallback != null) { 106 mCallback.onSystemStatusChanged(detectionFlags, status); 107 } 108 } 109 110 @Override getInterfaceVersion()111 public int getInterfaceVersion() { 112 return this.VERSION; 113 } 114 115 @Override getInterfaceHash()116 public String getInterfaceHash() { 117 return this.HASH; 118 } 119 } 120 121 private MockOasHal mMockHal; 122 private com.android.car.OccupantAwarenessService mOasService; 123 124 private CountDownLatch mStatusLatch; 125 private SystemStatusEvent mSystemStatus; 126 127 @Before setUp()128 public void setUp() { 129 Context context = ApplicationProvider.getApplicationContext(); 130 mMockHal = new MockOasHal(); 131 mOasService = new com.android.car.OccupantAwarenessService(context, mMockHal); 132 mOasService.init(); 133 134 resetStatus(); 135 } 136 137 @After tearDown()138 public void tearDown() { 139 mOasService.release(); 140 } 141 142 @Test testWithNoRegisteredListeners()143 public void testWithNoRegisteredListeners() throws Exception { 144 // Verify operation when no listeners are registered. 145 mMockHal.fireStatusEvent(IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.READY); 146 147 // Give some time for the asynchronous event to be propagated 148 pauseForEventPropagation(); 149 150 // Nothing should have been received. 151 assertThat(mSystemStatus).isNull(); 152 } 153 154 @Test testRegisterEventListener_returnsSystemStatusReady()155 public void testRegisterEventListener_returnsSystemStatusReady() throws Exception { 156 // Fire a status event and ensure it is received. 157 // "Presence status is ready" 158 IOccupantAwarenessEventCallback callback = registerCallbackToService(); 159 mMockHal.fireStatusEvent( 160 IOccupantAwareness.CAP_PRESENCE_DETECTION, OccupantAwarenessStatus.READY); 161 waitStatusReady(); 162 163 assertThat(mSystemStatus.detectionType) 164 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_PRESENCE); 165 assertThat(mSystemStatus.systemStatus).isEqualTo(SystemStatusEvent.SYSTEM_STATUS_READY); 166 167 mOasService.unregisterEventListener(callback); 168 } 169 170 @Test testRegisterEventListener_returnsSystemStatusFailure()171 public void testRegisterEventListener_returnsSystemStatusFailure() throws Exception { 172 // "Gaze status is failed" 173 IOccupantAwarenessEventCallback callback = registerCallbackToService(); 174 mMockHal.fireStatusEvent( 175 IOccupantAwareness.CAP_GAZE_DETECTION, OccupantAwarenessStatus.FAILURE); 176 waitStatusReady(); 177 178 assertThat(mSystemStatus.detectionType).isEqualTo(SystemStatusEvent.DETECTION_TYPE_GAZE); 179 assertThat(mSystemStatus.systemStatus) 180 .isEqualTo(SystemStatusEvent.SYSTEM_STATUS_SYSTEM_FAILURE); 181 182 mOasService.unregisterEventListener(callback); 183 } 184 185 @Test testRegisterEventListener_returnsSystemStatusNotReady()186 public void testRegisterEventListener_returnsSystemStatusNotReady() throws Exception { 187 // "Driver monitoring status is not-ready" 188 IOccupantAwarenessEventCallback callback = registerCallbackToService(); 189 mMockHal.fireStatusEvent( 190 IOccupantAwareness.CAP_DRIVER_MONITORING_DETECTION, 191 OccupantAwarenessStatus.NOT_INITIALIZED); 192 waitStatusReady(); 193 194 assertThat(mSystemStatus.detectionType) 195 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING); 196 assertThat(mSystemStatus.systemStatus) 197 .isEqualTo(SystemStatusEvent.SYSTEM_STATUS_NOT_READY); 198 199 mOasService.unregisterEventListener(callback); 200 } 201 202 @Test testRegisterEventListener_returnsSystemStatusNotSupported()203 public void testRegisterEventListener_returnsSystemStatusNotSupported() throws Exception { 204 // "None is non-supported" 205 IOccupantAwarenessEventCallback callback = registerCallbackToService(); 206 mMockHal.fireStatusEvent( 207 IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.NOT_SUPPORTED); 208 waitStatusReady(); 209 210 assertThat(mSystemStatus.detectionType).isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 211 assertThat(mSystemStatus.systemStatus) 212 .isEqualTo(SystemStatusEvent.SYSTEM_STATUS_NOT_SUPPORTED); 213 214 mOasService.unregisterEventListener(callback); 215 } 216 217 @Test test_unregisteredListeners()218 public void test_unregisteredListeners() throws Exception { 219 // Verify that listeners are successfully unregistered. 220 IOccupantAwarenessEventCallback callback = registerCallbackToService(); 221 222 // Unregister the registered listener. 223 mOasService.unregisterEventListener(callback); 224 225 // Fire some events. 226 mMockHal.fireStatusEvent(IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.READY); 227 mMockHal.fireStatusEvent( 228 IOccupantAwareness.CAP_GAZE_DETECTION, OccupantAwarenessStatus.READY); 229 mMockHal.fireStatusEvent( 230 IOccupantAwareness.CAP_DRIVER_MONITORING_DETECTION, OccupantAwarenessStatus.READY); 231 232 // Give some time for the asynchronous event to be propagated 233 pauseForEventPropagation(); 234 235 // Nothing should have been received. 236 assertThat(mSystemStatus).isNull(); 237 238 // Unregister a second time should log an error, but otherwise not cause any action. 239 mOasService.unregisterEventListener(callback); 240 } 241 242 @Test testGetCapabilityForRole_returnsAggregatedDriverStatus()243 public void testGetCapabilityForRole_returnsAggregatedDriverStatus() throws Exception { 244 assertThat(mOasService.getCapabilityForRole( 245 OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER)) 246 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_PRESENCE 247 | SystemStatusEvent.DETECTION_TYPE_GAZE 248 | SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING); 249 } 250 251 @Test testGetCapabilityForRole_returnsPresence()252 public void testGetCapabilityForRole_returnsPresence() throws Exception { 253 assertThat(mOasService.getCapabilityForRole( 254 OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER)) 255 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_PRESENCE); 256 } 257 258 @Test testGetCapabilityForRole_returnsNone_withRow2PassengerLeft()259 public void testGetCapabilityForRole_returnsNone_withRow2PassengerLeft() throws Exception { 260 assertThat(mOasService.getCapabilityForRole( 261 OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_LEFT)) 262 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 263 } 264 265 @Test testGetCapabilityForRole_returnsNone_withRow2PassengerCenter()266 public void testGetCapabilityForRole_returnsNone_withRow2PassengerCenter() throws Exception { 267 assertThat(mOasService.getCapabilityForRole( 268 OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_CENTER)) 269 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 270 } 271 272 @Test testGetCapabilityForRole_returnsNone_withRow2PassengerRight()273 public void testGetCapabilityForRole_returnsNone_withRow2PassengerRight() throws Exception { 274 assertThat(mOasService.getCapabilityForRole( 275 OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_RIGHT)) 276 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 277 } 278 279 @Test testGetCapabilityForRole_returnsNone_withOccupantNone()280 public void testGetCapabilityForRole_returnsNone_withOccupantNone() throws Exception { 281 assertThat(mOasService.getCapabilityForRole( 282 OccupantAwarenessDetection.VEHICLE_OCCUPANT_NONE)) 283 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 284 } 285 286 @Test testRegisterEventListener_returnsGraphNotRunningOnStart()287 public void testRegisterEventListener_returnsGraphNotRunningOnStart() 288 throws Exception { 289 // Should be not running on start (no clients are yet connected). 290 assertThat(mMockHal.isGraphRunning()).isFalse(); 291 } 292 293 @Test testRegisterEventListener_returnsGraphRunningWithOneListener()294 public void testRegisterEventListener_returnsGraphRunningWithOneListener() 295 throws Exception { 296 // Connect a client. Graph should be running. 297 IOccupantAwarenessEventCallback first_client = registerCallbackToService(); 298 299 assertThat(mMockHal.isGraphRunning()).isTrue(); 300 301 mOasService.unregisterEventListener(first_client); 302 } 303 304 @Test testRegisterEventListener_returnsGraphRunningWithTwoListeners()305 public void testRegisterEventListener_returnsGraphRunningWithTwoListeners() 306 throws Exception { 307 // Connect multiple (two) clients. Graph should be running. 308 IOccupantAwarenessEventCallback first_client = registerCallbackToService(); 309 IOccupantAwarenessEventCallback second_client = registerCallbackToService(); 310 311 assertThat(mMockHal.isGraphRunning()).isTrue(); 312 313 mOasService.unregisterEventListener(first_client); 314 mOasService.unregisterEventListener(second_client); 315 assertThat(mMockHal.isGraphRunning()).isFalse(); 316 } 317 318 @Test testUnregisterEventListener_returnsGraphNotRunningWithoutListeners()319 public void testUnregisterEventListener_returnsGraphNotRunningWithoutListeners() 320 throws Exception { 321 // Connect a client and disconnect it. Graph should be not running. 322 IOccupantAwarenessEventCallback first_client = registerCallbackToService(); 323 mOasService.unregisterEventListener(first_client); 324 325 assertThat(mMockHal.isGraphRunning()).isFalse(); 326 } 327 328 @Test testUnregisterEventListener_returnsGraphRunningWithListeners()329 public void testUnregisterEventListener_returnsGraphRunningWithListeners() 330 throws Exception { 331 // Connect multipe (two) clients and disconnect one. Graph should be running. 332 IOccupantAwarenessEventCallback first_client = registerCallbackToService(); 333 IOccupantAwarenessEventCallback second_client = registerCallbackToService(); 334 mOasService.unregisterEventListener(first_client); 335 336 assertThat(mMockHal.isGraphRunning()).isTrue(); 337 338 mOasService.unregisterEventListener(second_client); 339 assertThat(mMockHal.isGraphRunning()).isFalse(); 340 } 341 342 @Test testUnregisterEventListener_returnsGraphNotRunningAfterAllListenersRemoved()343 public void testUnregisterEventListener_returnsGraphNotRunningAfterAllListenersRemoved() 344 throws Exception { 345 // Connect multipe (two) clients and disconnect them all. Graph should not be running. 346 IOccupantAwarenessEventCallback first_client = registerCallbackToService(); 347 IOccupantAwarenessEventCallback second_client = registerCallbackToService(); 348 mOasService.unregisterEventListener(first_client); 349 mOasService.unregisterEventListener(second_client); 350 351 assertThat(mMockHal.isGraphRunning()).isFalse(); 352 } 353 354 /** Registers a listener to the service. */ registerCallbackToService()355 private IOccupantAwarenessEventCallback registerCallbackToService() { 356 IOccupantAwarenessEventCallback callback = 357 new IOccupantAwarenessEventCallback.Stub() { 358 @Override 359 public void onStatusChanged(SystemStatusEvent systemStatusEvent) { 360 mSystemStatus = systemStatusEvent; 361 mStatusLatch.countDown(); 362 } 363 364 @Override 365 public void onDetectionEvent(OccupantAwarenessDetection detection) { 366 } 367 }; 368 369 mOasService.registerEventListener(callback); 370 return callback; 371 } 372 373 /** Resets futures for testing. */ resetStatus()374 private void resetStatus() { 375 mStatusLatch = new CountDownLatch(1); 376 mSystemStatus = null; 377 } 378 waitStatusReady()379 private void waitStatusReady() throws Exception { 380 mStatusLatch.await(1L, TimeUnit.SECONDS); 381 } 382 pauseForEventPropagation()383 private void pauseForEventPropagation() { 384 try { 385 mStatusLatch.await(100L, TimeUnit.MILLISECONDS); 386 } catch (Exception e) { 387 } 388 } 389 } 390