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.OccupantDetection; 28 import android.hardware.automotive.occupant_awareness.OccupantDetections; 29 import android.hardware.automotive.occupant_awareness.Role; 30 import android.os.RemoteException; 31 import android.test.suitebuilder.annotation.MediumTest; 32 33 import androidx.test.core.app.ApplicationProvider; 34 import androidx.test.runner.AndroidJUnit4; 35 36 import junit.framework.TestCase; 37 38 import org.junit.After; 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 43 import java.util.concurrent.CompletableFuture; 44 import java.util.concurrent.TimeUnit; 45 46 @RunWith(AndroidJUnit4.class) 47 @MediumTest 48 public class OccupantAwarenessSystemServiceTest extends TestCase { 49 private static final int TIMESTAMP = 1234; // In milliseconds. 50 51 /** 52 * Mock implementation of {@link 53 * android.hardware.automotive.occupant_awareness.IOccupantAwareness} for testing the service 54 * and manager. 55 */ 56 private class MockOasHal 57 extends android.hardware.automotive.occupant_awareness.IOccupantAwareness.Stub { 58 private IOccupantAwarenessClientCallback mCallback; 59 private boolean mGraphIsRunning; 60 MockOasHal()61 MockOasHal() {} 62 63 /** Returns whether the mock graph is running. */ isGraphRunning()64 public boolean isGraphRunning() { 65 return mGraphIsRunning; 66 } 67 68 @Override getLatestDetection(OccupantDetections detections)69 public void getLatestDetection(OccupantDetections detections) {} 70 71 @Override setCallback(IOccupantAwarenessClientCallback callback)72 public void setCallback(IOccupantAwarenessClientCallback callback) { 73 mCallback = callback; 74 } 75 76 @Override getState(int occupantRole, int detectionCapability)77 public @OccupantAwarenessStatus byte getState(int occupantRole, int detectionCapability) { 78 return OccupantAwarenessStatus.READY; 79 } 80 81 @Override startDetection()82 public @OccupantAwarenessStatus byte startDetection() { 83 mGraphIsRunning = true; 84 return OccupantAwarenessStatus.READY; 85 } 86 87 @Override stopDetection()88 public @OccupantAwarenessStatus byte stopDetection() { 89 mGraphIsRunning = false; 90 return OccupantAwarenessStatus.READY; 91 } 92 93 @Override getCapabilityForRole(@ole int occupantRole)94 public int getCapabilityForRole(@Role int occupantRole) { 95 if (occupantRole == OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER) { 96 return SystemStatusEvent.DETECTION_TYPE_PRESENCE 97 | SystemStatusEvent.DETECTION_TYPE_GAZE 98 | SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING; 99 } else if (occupantRole 100 == OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER) { 101 return SystemStatusEvent.DETECTION_TYPE_PRESENCE; 102 } else { 103 return SystemStatusEvent.DETECTION_TYPE_NONE; 104 } 105 } 106 107 /** Causes a status event to be generated with the specified flags. */ fireStatusEvent(int detectionFlags, @OccupantAwarenessStatus byte status)108 public void fireStatusEvent(int detectionFlags, @OccupantAwarenessStatus byte status) 109 throws RemoteException { 110 if (mCallback != null) { 111 mCallback.onSystemStatusChanged(detectionFlags, status); 112 } 113 } 114 115 /** Causes a status event to be generated with the specified detection event data. */ fireDetectionEvent(OccupantAwarenessDetection detectionEvent)116 public void fireDetectionEvent(OccupantAwarenessDetection detectionEvent) 117 throws RemoteException { 118 if (mCallback != null) { 119 OccupantDetection detection = new OccupantDetection(); 120 121 OccupantDetections detections = new OccupantDetections(); 122 detections.timeStampMillis = TIMESTAMP; 123 detections.detections = new OccupantDetection[] {detection}; 124 mCallback.onDetectionEvent(detections); 125 } 126 } 127 128 @Override getInterfaceVersion()129 public int getInterfaceVersion() { 130 return this.VERSION; 131 } 132 133 @Override getInterfaceHash()134 public String getInterfaceHash() { 135 return this.HASH; 136 } 137 } 138 139 private MockOasHal mMockHal; 140 private com.android.car.OccupantAwarenessService mOasService; 141 142 private CompletableFuture<SystemStatusEvent> mFutureStatus; 143 private CompletableFuture<OccupantAwarenessDetection> mFutureDetection; 144 145 @Before setUp()146 public void setUp() { 147 Context context = ApplicationProvider.getApplicationContext(); 148 mMockHal = new MockOasHal(); 149 mOasService = new com.android.car.OccupantAwarenessService(context, mMockHal); 150 mOasService.init(); 151 152 resetFutures(); 153 } 154 155 @After tearDown()156 public void tearDown() { 157 mOasService.release(); 158 } 159 160 @Test testWithNoRegisteredListeners()161 public void testWithNoRegisteredListeners() throws Exception { 162 // Verify operation when no listeners are registered. 163 mMockHal.fireStatusEvent(IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.READY); 164 165 // Nothing should have been received. 166 assertThat(mFutureStatus.isDone()).isFalse(); 167 assertThat(mFutureDetection.isDone()).isFalse(); 168 } 169 170 @Test testStatusEventsWithRegisteredListeners()171 public void testStatusEventsWithRegisteredListeners() throws Exception { 172 // Verify correct operation when a listener has been registered. 173 registerCallbackToService(); 174 SystemStatusEvent result; 175 176 // Fire a status event and ensure it is received. 177 // "Presence status is ready" 178 resetFutures(); 179 mMockHal.fireStatusEvent( 180 IOccupantAwareness.CAP_PRESENCE_DETECTION, OccupantAwarenessStatus.READY); 181 182 result = mFutureStatus.get(1, TimeUnit.SECONDS); 183 assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_PRESENCE); 184 assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_READY); 185 186 // "Gaze status is failed" 187 resetFutures(); 188 mMockHal.fireStatusEvent( 189 IOccupantAwareness.CAP_GAZE_DETECTION, OccupantAwarenessStatus.FAILURE); 190 191 result = mFutureStatus.get(1, TimeUnit.SECONDS); 192 assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_GAZE); 193 assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_SYSTEM_FAILURE); 194 195 // "Driver monitoring status is not-ready" 196 resetFutures(); 197 mMockHal.fireStatusEvent( 198 IOccupantAwareness.CAP_DRIVER_MONITORING_DETECTION, 199 OccupantAwarenessStatus.NOT_INITIALIZED); 200 201 result = mFutureStatus.get(1, TimeUnit.SECONDS); 202 assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING); 203 assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_NOT_READY); 204 205 // "None is non-supported" 206 resetFutures(); 207 mMockHal.fireStatusEvent( 208 IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.NOT_SUPPORTED); 209 210 result = mFutureStatus.get(1, TimeUnit.SECONDS); 211 assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_NONE); 212 assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_NOT_SUPPORTED); 213 } 214 215 @Test test_unregisteredListeners()216 public void test_unregisteredListeners() throws Exception { 217 // Verify that listeners are successfully unregistered. 218 IOccupantAwarenessEventCallback callback = registerCallbackToService(); 219 220 // Unregister the registered listener. 221 mOasService.unregisterEventListener(callback); 222 223 // Fire some events. 224 mMockHal.fireStatusEvent(IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.READY); 225 mMockHal.fireStatusEvent( 226 IOccupantAwareness.CAP_GAZE_DETECTION, OccupantAwarenessStatus.READY); 227 mMockHal.fireStatusEvent( 228 IOccupantAwareness.CAP_DRIVER_MONITORING_DETECTION, OccupantAwarenessStatus.READY); 229 230 // Nothing should have been received. 231 assertThat(mFutureStatus.isDone()).isFalse(); 232 assertThat(mFutureDetection.isDone()).isFalse(); 233 234 // Unregister a second time should log an error, but otherwise not cause any action. 235 mOasService.unregisterEventListener(callback); 236 } 237 238 @Test test_getCapabilityForRole()239 public void test_getCapabilityForRole() throws Exception { 240 assertThat( 241 mOasService.getCapabilityForRole( 242 OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER)) 243 .isEqualTo( 244 SystemStatusEvent.DETECTION_TYPE_PRESENCE 245 | SystemStatusEvent.DETECTION_TYPE_GAZE 246 | SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING); 247 248 assertThat( 249 mOasService.getCapabilityForRole( 250 OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER)) 251 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_PRESENCE); 252 253 assertThat( 254 mOasService.getCapabilityForRole( 255 OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_RIGHT)) 256 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 257 258 assertThat( 259 mOasService.getCapabilityForRole( 260 OccupantAwarenessDetection.VEHICLE_OCCUPANT_NONE)) 261 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE); 262 } 263 264 @Test test_serviceStartsAndStopGraphWithListeners()265 public void test_serviceStartsAndStopGraphWithListeners() throws Exception { 266 // Verify that the service starts the detection graph when the first client connects, and 267 // stop when the last client disconnects. 268 269 // Should be not running on start (no clients are yet connected). 270 assertThat(mMockHal.isGraphRunning()).isFalse(); 271 272 // Connect a client. Graph should be running. 273 IOccupantAwarenessEventCallback first_client = registerCallbackToService(); 274 assertThat(mMockHal.isGraphRunning()).isTrue(); 275 276 // Connect a second client. Graph should continue running. 277 IOccupantAwarenessEventCallback second_client = registerCallbackToService(); 278 assertThat(mMockHal.isGraphRunning()).isTrue(); 279 280 // Remove the first client. Graph should continue to run since a client still remains. 281 mOasService.unregisterEventListener(first_client); 282 assertThat(mMockHal.isGraphRunning()).isTrue(); 283 284 // Remove the second client. Graph should now stop since all clients have now closed. 285 mOasService.unregisterEventListener(second_client); 286 assertThat(mMockHal.isGraphRunning()).isFalse(); 287 } 288 289 /** Registers a listener to the service. */ registerCallbackToService()290 private IOccupantAwarenessEventCallback registerCallbackToService() { 291 IOccupantAwarenessEventCallback callback = 292 new IOccupantAwarenessEventCallback.Stub() { 293 @Override 294 public void onStatusChanged(SystemStatusEvent systemStatusEvent) { 295 mFutureStatus.complete(systemStatusEvent); 296 } 297 298 public void onDetectionEvent(OccupantAwarenessDetection detectionEvent) { 299 mFutureDetection.complete(detectionEvent); 300 } 301 }; 302 303 mOasService.registerEventListener(callback); 304 return callback; 305 } 306 307 /** Resets futures for testing. */ resetFutures()308 private void resetFutures() { 309 mFutureStatus = new CompletableFuture<>(); 310 mFutureDetection = new CompletableFuture<>(); 311 } 312 } 313