1 /* 2 * Copyright (C) 2018 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 static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertTrue; 23 24 import android.car.Car; 25 import android.car.drivingstate.CarDrivingStateEvent; 26 import android.car.drivingstate.CarDrivingStateManager; 27 import android.car.drivingstate.CarUxRestrictions; 28 import android.car.drivingstate.CarUxRestrictionsManager; 29 import android.car.hardware.CarPropertyValue; 30 import android.hardware.automotive.vehicle.VehicleGear; 31 import android.hardware.automotive.vehicle.VehicleIgnitionState; 32 import android.hardware.automotive.vehicle.VehicleProperty; 33 import android.os.SystemClock; 34 import android.util.Log; 35 36 import androidx.test.ext.junit.runners.AndroidJUnit4; 37 import androidx.test.filters.SmallTest; 38 39 import com.android.car.hal.test.AidlVehiclePropValueBuilder; 40 import com.android.internal.annotations.GuardedBy; 41 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 @RunWith(AndroidJUnit4.class) 46 @SmallTest 47 public class CarDrivingRestrictionsTest extends MockedCarTestBase { 48 private static final String TAG = CarDrivingRestrictionsTest.class.getSimpleName(); 49 private CarDrivingStateManager mCarDrivingStateManager; 50 private CarUxRestrictionsManager mCarUxRManager; 51 private DrivingStateListener mDrivingStateListener; 52 // Currently set restrictions currently set in car_ux_restrictions_map.xml 53 private static final int UX_RESTRICTIONS_MOVING = CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD 54 | CarUxRestrictions.UX_RESTRICTIONS_NO_FILTERING 55 | CarUxRestrictions.UX_RESTRICTIONS_LIMIT_STRING_LENGTH 56 | CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD 57 | CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO 58 | CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT 59 | CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP 60 | CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE; 61 private static final int UX_RESTRICTIONS_IDLE = CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO; 62 63 @Override configureMockedHal()64 protected void configureMockedHal() { 65 addAidlProperty(VehicleProperty.PERF_VEHICLE_SPEED, AidlVehiclePropValueBuilder 66 .newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 67 .addFloatValues(0f) 68 .build()); 69 addAidlProperty(VehicleProperty.PARKING_BRAKE_ON, AidlVehiclePropValueBuilder 70 .newBuilder(VehicleProperty.PARKING_BRAKE_ON) 71 .setBooleanValue(false) 72 .build()); 73 addAidlProperty(VehicleProperty.GEAR_SELECTION, AidlVehiclePropValueBuilder 74 .newBuilder(VehicleProperty.GEAR_SELECTION) 75 .addIntValues(0) 76 .build()); 77 addAidlProperty(VehicleProperty.IGNITION_STATE, AidlVehiclePropValueBuilder 78 .newBuilder(VehicleProperty.IGNITION_STATE) 79 .addIntValues(0) 80 .build()); 81 } 82 83 @Override setUp()84 public void setUp() throws Exception { 85 super.setUp(); 86 mCarDrivingStateManager = (CarDrivingStateManager) getCar() 87 .getCarManager(Car.CAR_DRIVING_STATE_SERVICE); 88 mCarUxRManager = (CarUxRestrictionsManager) getCar() 89 .getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); 90 mDrivingStateListener = new DrivingStateListener(); 91 mCarDrivingStateManager.registerListener(mDrivingStateListener); 92 mCarUxRManager.registerListener(mDrivingStateListener); 93 } 94 95 @Override tearDown()96 public void tearDown() throws Exception { 97 super.tearDown(); 98 mCarDrivingStateManager.unregisterListener(); 99 mCarUxRManager.unregisterListener(); 100 } 101 aidlInjectIntEvent(int propertyId, int value, int status)102 private void aidlInjectIntEvent(int propertyId, int value, int status) { 103 getAidlMockedVehicleHal().injectEvent( 104 AidlVehiclePropValueBuilder.newBuilder(propertyId) 105 .addIntValues(value) 106 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 107 .setStatus(status) 108 .build()); 109 } 110 aidlInjectVehicleSpeedEvent(float value, int status)111 private void aidlInjectVehicleSpeedEvent(float value, int status) { 112 getAidlMockedVehicleHal().injectEvent( 113 AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 114 .addFloatValues(value) 115 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 116 .setStatus(status) 117 .build()); 118 } 119 aidlInjectParkingBrakeEvent(boolean value, int status)120 private void aidlInjectParkingBrakeEvent(boolean value, int status) { 121 getAidlMockedVehicleHal().injectEvent( 122 AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 123 .setBooleanValue(value) 124 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 125 .setStatus(status) 126 .build()); 127 } 128 129 @Test testDrivingStateChange()130 public void testDrivingStateChange() throws InterruptedException { 131 CarDrivingStateEvent drivingEvent; 132 CarUxRestrictions restrictions; 133 // With no gear value available, driving state should be unknown 134 mDrivingStateListener.reset(); 135 // Test Parked state and corresponding restrictions based on car_ux_restrictions_map.xml 136 Log.d(TAG, "Injecting gear park"); 137 aidlInjectIntEvent(VehicleProperty.GEAR_SELECTION, VehicleGear.GEAR_PARK, 138 CarPropertyValue.STATUS_AVAILABLE); 139 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 140 assertNotNull(drivingEvent); 141 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 142 143 Log.d(TAG, "Injecting speed 0"); 144 aidlInjectVehicleSpeedEvent(0.0f, CarPropertyValue.STATUS_AVAILABLE); 145 146 // Switch gear to drive. Driving state changes to Idling but the UX restrictions don't 147 // change between parked and idling. 148 mDrivingStateListener.reset(); 149 Log.d(TAG, "Injecting gear drive"); 150 aidlInjectIntEvent(VehicleProperty.GEAR_SELECTION, VehicleGear.GEAR_DRIVE, 151 CarPropertyValue.STATUS_AVAILABLE); 152 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 153 assertNotNull(drivingEvent); 154 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING); 155 156 // Test Moving state and corresponding restrictions based on car_ux_restrictions_map.xml 157 mDrivingStateListener.reset(); 158 Log.d(TAG, "Injecting speed 30"); 159 aidlInjectVehicleSpeedEvent(30.0f, CarPropertyValue.STATUS_AVAILABLE); 160 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 161 assertNotNull(drivingEvent); 162 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_MOVING); 163 restrictions = mDrivingStateListener.waitForUxRestrictionsChange(UX_RESTRICTIONS_MOVING); 164 assertNotNull(restrictions); 165 assertTrue(restrictions.isRequiresDistractionOptimization()); 166 assertThat(restrictions.getActiveRestrictions()).isEqualTo(UX_RESTRICTIONS_MOVING); 167 168 // Test Idling state and corresponding restrictions based on car_ux_restrictions_map.xml 169 mDrivingStateListener.reset(); 170 Log.d(TAG, "Injecting speed 0"); 171 aidlInjectVehicleSpeedEvent(0.0f, CarPropertyValue.STATUS_AVAILABLE); 172 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 173 assertNotNull(drivingEvent); 174 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING); 175 restrictions = mDrivingStateListener.waitForUxRestrictionsChange(UX_RESTRICTIONS_IDLE); 176 assertNotNull(restrictions); 177 assertTrue(restrictions.isRequiresDistractionOptimization()); 178 assertThat(restrictions.getActiveRestrictions()).isEqualTo(UX_RESTRICTIONS_IDLE); 179 180 // Test Moving state and corresponding restrictions when driving in reverse. 181 Log.d(TAG, "Injecting gear reverse"); 182 aidlInjectIntEvent(VehicleProperty.GEAR_SELECTION, VehicleGear.GEAR_REVERSE, 183 CarPropertyValue.STATUS_AVAILABLE); 184 185 mDrivingStateListener.reset(); 186 Log.d(TAG, "Injecting speed -10"); 187 aidlInjectVehicleSpeedEvent(-10.0f, CarPropertyValue.STATUS_AVAILABLE); 188 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 189 assertNotNull(drivingEvent); 190 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_MOVING); 191 restrictions = mDrivingStateListener.waitForUxRestrictionsChange(UX_RESTRICTIONS_MOVING); 192 assertNotNull(restrictions); 193 assertTrue(restrictions.isRequiresDistractionOptimization()); 194 assertThat(restrictions.getActiveRestrictions()).isEqualTo(UX_RESTRICTIONS_MOVING); 195 196 // Apply Parking brake. Supported gears is not provided in this test and hence 197 // Automatic transmission should be assumed and hence parking brake state should not 198 // make a difference to the driving state. 199 mDrivingStateListener.reset(); 200 Log.d(TAG, "Injecting parking brake on"); 201 aidlInjectParkingBrakeEvent(true, CarPropertyValue.STATUS_AVAILABLE); 202 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 203 assertNull(drivingEvent); 204 } 205 206 @Test testDrivingStateChangeForMalformedInputs()207 public void testDrivingStateChangeForMalformedInputs() throws InterruptedException { 208 CarDrivingStateEvent drivingEvent; 209 CarUxRestrictions restrictions; 210 211 // Start with gear = park and speed = 0 to begin with a known state. 212 mDrivingStateListener.reset(); 213 Log.d(TAG, "Injecting gear park"); 214 aidlInjectIntEvent(VehicleProperty.GEAR_SELECTION, VehicleGear.GEAR_PARK, 215 CarPropertyValue.STATUS_AVAILABLE); 216 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 217 assertNotNull(drivingEvent); 218 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 219 220 Log.d(TAG, "Injecting speed 0"); 221 aidlInjectVehicleSpeedEvent(0.0f, CarPropertyValue.STATUS_AVAILABLE); 222 223 // Inject an invalid gear. Since speed is still valid, idling will be the expected 224 // driving state 225 mDrivingStateListener.reset(); 226 Log.d(TAG, "Injecting gear -1 with status unavailable"); 227 aidlInjectIntEvent(VehicleProperty.GEAR_SELECTION, -1, CarPropertyValue.STATUS_UNAVAILABLE); 228 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 229 assertNotNull(drivingEvent); 230 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING); 231 232 // Now, send in an invalid speed value as well, now the driving state will be unknown and 233 // the UX restrictions will change to fully restricted. 234 mDrivingStateListener.reset(); 235 Log.d(TAG, "Injecting speed -1 with status unavailable"); 236 aidlInjectVehicleSpeedEvent(-1.0f, CarPropertyValue.STATUS_UNAVAILABLE); 237 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 238 assertNotNull(drivingEvent); 239 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN); 240 restrictions = mDrivingStateListener.waitForUxRestrictionsChange( 241 CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED); 242 assertNotNull(restrictions); 243 assertTrue(restrictions.isRequiresDistractionOptimization()); 244 assertThat(restrictions.getActiveRestrictions()) 245 .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED); 246 } 247 setupDrivingStateUnknown()248 private CarDrivingStateEvent setupDrivingStateUnknown() throws InterruptedException { 249 CarDrivingStateEvent drivingEvent; 250 mDrivingStateListener.reset(); 251 252 // Inject an invalid gear. 253 Log.d(TAG, "Injecting gear -1 with status unavailable"); 254 aidlInjectIntEvent(VehicleProperty.GEAR_SELECTION, -1, CarPropertyValue.STATUS_UNAVAILABLE); 255 256 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 257 assertNull(drivingEvent); 258 259 // Now, send in an invalid speed value as well, now the driving state will be unknown. 260 mDrivingStateListener.reset(); 261 Log.d(TAG, "Injecting speed -1 with status unavailable"); 262 aidlInjectVehicleSpeedEvent(-1.0f, CarPropertyValue.STATUS_UNAVAILABLE); 263 264 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 265 assertNotNull(drivingEvent); 266 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN); 267 return drivingEvent; 268 } 269 270 @Test testGearUnknown_speedUnknown_ignitionStateOff_drivingStateInferredAsParked()271 public void testGearUnknown_speedUnknown_ignitionStateOff_drivingStateInferredAsParked() 272 throws InterruptedException { 273 setupDrivingStateUnknown(); 274 275 mDrivingStateListener.reset(); 276 Log.d(TAG, "Injecting ignition state off"); 277 aidlInjectIntEvent(VehicleProperty.IGNITION_STATE, VehicleIgnitionState.OFF, 278 CarPropertyValue.STATUS_AVAILABLE); 279 280 CarDrivingStateEvent drivingEvent; 281 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 282 assertNotNull(drivingEvent); 283 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 284 } 285 286 @Test testGearUnknown_speedUnknown_ignitionStateAcc_drivingStateInferredAsParked()287 public void testGearUnknown_speedUnknown_ignitionStateAcc_drivingStateInferredAsParked() 288 throws InterruptedException { 289 setupDrivingStateUnknown(); 290 291 mDrivingStateListener.reset(); 292 Log.d(TAG, "Injecting ignition state acc"); 293 aidlInjectIntEvent(VehicleProperty.IGNITION_STATE, VehicleIgnitionState.ACC, 294 CarPropertyValue.STATUS_AVAILABLE); 295 296 CarDrivingStateEvent drivingEvent; 297 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 298 assertNotNull(drivingEvent); 299 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 300 } 301 302 @Test testGearUnknown_speedUnknown_ignitionStateLock_drivingStateInferredAsParked()303 public void testGearUnknown_speedUnknown_ignitionStateLock_drivingStateInferredAsParked() 304 throws InterruptedException { 305 setupDrivingStateUnknown(); 306 307 mDrivingStateListener.reset(); 308 Log.d(TAG, "Injecting ignition state lock"); 309 aidlInjectIntEvent(VehicleProperty.IGNITION_STATE, VehicleIgnitionState.LOCK, 310 CarPropertyValue.STATUS_AVAILABLE); 311 312 CarDrivingStateEvent drivingEvent; 313 drivingEvent = mDrivingStateListener.waitForDrivingStateChange(); 314 assertNotNull(drivingEvent); 315 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 316 } 317 318 @Test testGearUnknown_speedUnknown_ignitionStateOn_drivingStateInferredAsUnknown()319 public void testGearUnknown_speedUnknown_ignitionStateOn_drivingStateInferredAsUnknown() 320 throws InterruptedException { 321 CarDrivingStateEvent drivingEvent; 322 drivingEvent = setupDrivingStateUnknown(); 323 CarUxRestrictions restrictions; 324 restrictions = mDrivingStateListener.waitForUxRestrictionsChange( 325 CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED); 326 327 mDrivingStateListener.reset(); 328 Log.d(TAG, "Injecting ignition state on"); 329 aidlInjectIntEvent(VehicleProperty.IGNITION_STATE, VehicleIgnitionState.ON, 330 CarPropertyValue.STATUS_AVAILABLE); 331 332 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN); 333 assertNotNull(restrictions); 334 assertTrue(restrictions.isRequiresDistractionOptimization()); 335 assertThat(restrictions.getActiveRestrictions()) 336 .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED); 337 } 338 339 /** 340 * Callback function we register for driving state update notifications. 341 */ 342 private static final class DrivingStateListener implements 343 CarDrivingStateManager.CarDrivingStateEventListener, 344 CarUxRestrictionsManager.OnUxRestrictionsChangedListener { 345 private final Object mDrivingStateLock = new Object(); 346 @GuardedBy("mDrivingStateLock") 347 private CarDrivingStateEvent mLastEvent = null; 348 private final Object mUxRLock = new Object(); 349 @GuardedBy("mUxRLock") 350 private CarUxRestrictions mLastRestrictions = null; 351 reset()352 void reset() { 353 synchronized (mDrivingStateLock) { 354 mLastEvent = null; 355 } 356 synchronized (mUxRLock) { 357 mLastRestrictions = null; 358 } 359 } 360 361 // Returns True to indicate receipt of a driving state event. False indicates a timeout. waitForDrivingStateChange()362 CarDrivingStateEvent waitForDrivingStateChange() throws InterruptedException { 363 long start = SystemClock.elapsedRealtime(); 364 365 synchronized (mDrivingStateLock) { 366 while (mLastEvent == null 367 && (start + DEFAULT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) { 368 mDrivingStateLock.wait(100L); 369 } 370 return mLastEvent; 371 } 372 } 373 374 @Override onDrivingStateChanged(CarDrivingStateEvent event)375 public void onDrivingStateChanged(CarDrivingStateEvent event) { 376 Log.d(TAG, "onDrivingStateChanged, event: " + event.eventValue); 377 synchronized (mDrivingStateLock) { 378 // We're going to hold a reference to this object 379 mLastEvent = event; 380 mDrivingStateLock.notifyAll(); 381 } 382 } 383 waitForUxRestrictionsChange(int expectedRestrictions)384 CarUxRestrictions waitForUxRestrictionsChange(int expectedRestrictions) 385 throws InterruptedException { 386 long start = SystemClock.elapsedRealtime(); 387 synchronized (mUxRLock) { 388 while ((mLastRestrictions == null 389 || mLastRestrictions.getActiveRestrictions() != expectedRestrictions) 390 && (start + DEFAULT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) { 391 mUxRLock.wait(100L); 392 } 393 return mLastRestrictions; 394 } 395 } 396 397 @Override onUxRestrictionsChanged(CarUxRestrictions restrictions)398 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) { 399 Log.d(TAG, "onUxRestrictionsChanged, restrictions: " 400 + restrictions.getActiveRestrictions()); 401 synchronized (mUxRLock) { 402 mLastRestrictions = restrictions; 403 mUxRLock.notifyAll(); 404 } 405 } 406 } 407 } 408