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.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 25 import android.car.Car; 26 import android.car.drivingstate.CarDrivingStateEvent; 27 import android.car.drivingstate.CarDrivingStateManager; 28 import android.car.drivingstate.CarUxRestrictions; 29 import android.car.drivingstate.CarUxRestrictionsManager; 30 import android.hardware.automotive.vehicle.V2_0.VehicleGear; 31 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 32 import android.os.SystemClock; 33 import android.util.Log; 34 35 import androidx.test.filters.SmallTest; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.car.vehiclehal.VehiclePropValueBuilder; 39 import com.android.internal.annotations.GuardedBy; 40 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 44 @RunWith(AndroidJUnit4.class) 45 @SmallTest 46 public class CarDrivingRestrictionsTest extends MockedCarTestBase { 47 private static final String TAG = CarDrivingRestrictionsTest.class 48 .getSimpleName(); 49 private CarDrivingStateManager mCarDrivingStateManager; 50 private CarUxRestrictionsManager mCarUxRManager; 51 // Currently set restrictions currently set in car_ux_restrictions_map.xml 52 private static final int UX_RESTRICTIONS_MOVING = CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD 53 | CarUxRestrictions.UX_RESTRICTIONS_NO_FILTERING 54 | CarUxRestrictions.UX_RESTRICTIONS_LIMIT_STRING_LENGTH 55 | CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD 56 | CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO 57 | CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT 58 | CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP 59 | CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE; 60 61 62 @Override configureMockedHal()63 protected synchronized void configureMockedHal() { 64 addProperty(VehicleProperty.PERF_VEHICLE_SPEED, VehiclePropValueBuilder 65 .newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 66 .addFloatValue(0f) 67 .build()); 68 addProperty(VehicleProperty.PARKING_BRAKE_ON, VehiclePropValueBuilder 69 .newBuilder(VehicleProperty.PARKING_BRAKE_ON) 70 .setBooleanValue(false) 71 .build()); 72 addProperty(VehicleProperty.GEAR_SELECTION, VehiclePropValueBuilder 73 .newBuilder(VehicleProperty.GEAR_SELECTION) 74 .addIntValue(0) 75 .build()); 76 } 77 78 @Override setUp()79 public void setUp() throws Exception { 80 super.setUp(); 81 mCarDrivingStateManager = (CarDrivingStateManager) getCar() 82 .getCarManager(Car.CAR_DRIVING_STATE_SERVICE); 83 mCarUxRManager = (CarUxRestrictionsManager) getCar() 84 .getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); 85 } 86 87 @Test testDrivingStateChange()88 public void testDrivingStateChange() throws InterruptedException { 89 CarDrivingStateEvent drivingEvent; 90 CarUxRestrictions restrictions; 91 DrivingStateListener listener = new DrivingStateListener(); 92 mCarDrivingStateManager.registerListener(listener); 93 mCarUxRManager.registerListener(listener); 94 // With no gear value available, driving state should be unknown 95 listener.reset(); 96 // Test Parked state and corresponding restrictions based on car_ux_restrictions_map.xml 97 Log.d(TAG, "Injecting gear park"); 98 getMockedVehicleHal().injectEvent( 99 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION) 100 .addIntValue(VehicleGear.GEAR_PARK) 101 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 102 .build()); 103 drivingEvent = listener.waitForDrivingStateChange(); 104 assertNotNull(drivingEvent); 105 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 106 107 Log.d(TAG, "Injecting speed 0"); 108 getMockedVehicleHal().injectEvent( 109 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 110 .addFloatValue(0.0f) 111 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 112 .build()); 113 114 // Switch gear to drive. Driving state changes to Idling but the UX restrictions don't 115 // change between parked and idling. 116 listener.reset(); 117 Log.d(TAG, "Injecting gear drive"); 118 getMockedVehicleHal().injectEvent( 119 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION) 120 .addIntValue(VehicleGear.GEAR_DRIVE) 121 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 122 .build()); 123 drivingEvent = listener.waitForDrivingStateChange(); 124 assertNotNull(drivingEvent); 125 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING); 126 127 // Test Moving state and corresponding restrictions based on car_ux_restrictions_map.xml 128 listener.reset(); 129 Log.d(TAG, "Injecting speed 30"); 130 getMockedVehicleHal().injectEvent( 131 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 132 .addFloatValue(30.0f) 133 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 134 .build()); 135 drivingEvent = listener.waitForDrivingStateChange(); 136 assertNotNull(drivingEvent); 137 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_MOVING); 138 restrictions = listener.waitForUxRestrictionsChange(); 139 assertNotNull(restrictions); 140 assertTrue(restrictions.isRequiresDistractionOptimization()); 141 assertThat(restrictions.getActiveRestrictions()).isEqualTo(UX_RESTRICTIONS_MOVING); 142 143 // Test Idling state and corresponding restrictions based on car_ux_restrictions_map.xml 144 listener.reset(); 145 Log.d(TAG, "Injecting speed 0"); 146 getMockedVehicleHal().injectEvent( 147 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 148 .addFloatValue(0.0f) 149 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 150 .build()); 151 drivingEvent = listener.waitForDrivingStateChange(); 152 assertNotNull(drivingEvent); 153 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING); 154 restrictions = listener.waitForUxRestrictionsChange(); 155 assertNotNull(restrictions); 156 assertFalse(restrictions.isRequiresDistractionOptimization()); 157 assertThat(restrictions.getActiveRestrictions()) 158 .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_BASELINE); 159 160 // Apply Parking brake. Supported gears is not provided in this test and hence 161 // Automatic transmission should be assumed and hence parking brake state should not 162 // make a difference to the driving state. 163 listener.reset(); 164 Log.d(TAG, "Injecting parking brake on"); 165 getMockedVehicleHal().injectEvent( 166 VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 167 .setBooleanValue(true) 168 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 169 .build()); 170 drivingEvent = listener.waitForDrivingStateChange(); 171 assertNull(drivingEvent); 172 173 mCarDrivingStateManager.unregisterListener(); 174 mCarUxRManager.unregisterListener(); 175 } 176 177 @Test testDrivingStateChangeForMalformedInputs()178 public void testDrivingStateChangeForMalformedInputs() throws InterruptedException { 179 CarDrivingStateEvent drivingEvent; 180 CarUxRestrictions restrictions; 181 DrivingStateListener listener = new DrivingStateListener(); 182 mCarDrivingStateManager.registerListener(listener); 183 mCarUxRManager.registerListener(listener); 184 185 // Start with gear = park and speed = 0 to begin with a known state. 186 listener.reset(); 187 Log.d(TAG, "Injecting gear park"); 188 getMockedVehicleHal().injectEvent( 189 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION) 190 .addIntValue(VehicleGear.GEAR_PARK) 191 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 192 .build()); 193 drivingEvent = listener.waitForDrivingStateChange(); 194 assertNotNull(drivingEvent); 195 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED); 196 197 Log.d(TAG, "Injecting speed 0"); 198 getMockedVehicleHal().injectEvent( 199 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 200 .addFloatValue(0.0f) 201 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 202 .build()); 203 204 // Inject an invalid gear. Since speed is still valid, idling will be the expected 205 // driving state 206 listener.reset(); 207 Log.d(TAG, "Injecting gear -1"); 208 getMockedVehicleHal().injectEvent( 209 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION) 210 .addIntValue(-1) 211 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 212 .build()); 213 drivingEvent = listener.waitForDrivingStateChange(); 214 assertNotNull(drivingEvent); 215 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING); 216 217 // Now, send in an invalid speed value as well, now the driving state will be unknown and 218 // the UX restrictions will change to fully restricted. 219 listener.reset(); 220 Log.d(TAG, "Injecting speed -1"); 221 getMockedVehicleHal().injectEvent( 222 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 223 .addFloatValue(-1.0f) 224 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 225 .build()); 226 drivingEvent = listener.waitForDrivingStateChange(); 227 assertNotNull(drivingEvent); 228 assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN); 229 restrictions = listener.waitForUxRestrictionsChange(); 230 assertNotNull(restrictions); 231 assertTrue(restrictions.isRequiresDistractionOptimization()); 232 assertThat(restrictions.getActiveRestrictions()) 233 .isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED); 234 mCarDrivingStateManager.unregisterListener(); 235 mCarUxRManager.unregisterListener(); 236 } 237 238 /** 239 * Callback function we register for driving state update notifications. 240 */ 241 private class DrivingStateListener implements 242 CarDrivingStateManager.CarDrivingStateEventListener, 243 CarUxRestrictionsManager.OnUxRestrictionsChangedListener { 244 private final Object mDrivingStateLock = new Object(); 245 @GuardedBy("mDrivingStateLock") 246 private CarDrivingStateEvent mLastEvent = null; 247 private final Object mUxRLock = new Object(); 248 @GuardedBy("mUxRLock") 249 private CarUxRestrictions mLastRestrictions = null; 250 reset()251 void reset() { 252 mLastEvent = null; 253 mLastRestrictions = null; 254 } 255 256 // Returns True to indicate receipt of a driving state event. False indicates a timeout. waitForDrivingStateChange()257 CarDrivingStateEvent waitForDrivingStateChange() throws InterruptedException { 258 long start = SystemClock.elapsedRealtime(); 259 260 synchronized (mDrivingStateLock) { 261 while (mLastEvent == null 262 && (start + DEFAULT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) { 263 mDrivingStateLock.wait(100L); 264 } 265 return mLastEvent; 266 } 267 } 268 269 @Override onDrivingStateChanged(CarDrivingStateEvent event)270 public void onDrivingStateChanged(CarDrivingStateEvent event) { 271 Log.d(TAG, "onDrivingStateChanged, event: " + event.eventValue); 272 synchronized (mDrivingStateLock) { 273 // We're going to hold a reference to this object 274 mLastEvent = event; 275 mDrivingStateLock.notify(); 276 } 277 } 278 waitForUxRestrictionsChange()279 CarUxRestrictions waitForUxRestrictionsChange() throws InterruptedException { 280 long start = SystemClock.elapsedRealtime(); 281 synchronized (mUxRLock) { 282 while (mLastRestrictions == null 283 && (start + DEFAULT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) { 284 mUxRLock.wait(100L); 285 } 286 } 287 return mLastRestrictions; 288 } 289 290 @Override onUxRestrictionsChanged(CarUxRestrictions restrictions)291 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) { 292 Log.d(TAG, "onUxRestrictionsChanged, restrictions: " 293 + restrictions.getActiveRestrictions()); 294 synchronized (mUxRLock) { 295 mLastRestrictions = restrictions; 296 mUxRLock.notify(); 297 } 298 } 299 } 300 } 301