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