blob: cf730ecb7b85a50a59b1deab4d34e167fd7c153e [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.car.Car;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.hardware.automotive.vehicle.V2_0.VehicleGear;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
import com.android.internal.annotations.GuardedBy;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CarDrivingRestrictionsTest extends MockedCarTestBase {
private static final String TAG = CarDrivingRestrictionsTest.class
.getSimpleName();
private CarDrivingStateManager mCarDrivingStateManager;
private CarUxRestrictionsManager mCarUxRManager;
// Currently set restrictions currently set in car_ux_restrictions_map.xml
private static final int UX_RESTRICTIONS_MOVING = CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD
| CarUxRestrictions.UX_RESTRICTIONS_NO_FILTERING
| CarUxRestrictions.UX_RESTRICTIONS_LIMIT_STRING_LENGTH
| CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD
| CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO
| CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT
| CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP
| CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE;
@Override
protected synchronized void configureMockedHal() {
addProperty(VehicleProperty.PERF_VEHICLE_SPEED, VehiclePropValueBuilder
.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(0f)
.build());
addProperty(VehicleProperty.PARKING_BRAKE_ON, VehiclePropValueBuilder
.newBuilder(VehicleProperty.PARKING_BRAKE_ON)
.setBooleanValue(false)
.build());
addProperty(VehicleProperty.GEAR_SELECTION, VehiclePropValueBuilder
.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(0)
.build());
}
@Override
public void setUp() throws Exception {
super.setUp();
mCarDrivingStateManager = (CarDrivingStateManager) getCar()
.getCarManager(Car.CAR_DRIVING_STATE_SERVICE);
mCarUxRManager = (CarUxRestrictionsManager) getCar()
.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
}
@Test
public void testDrivingStateChange() throws InterruptedException {
CarDrivingStateEvent drivingEvent;
CarUxRestrictions restrictions;
DrivingStateListener listener = new DrivingStateListener();
mCarDrivingStateManager.registerListener(listener);
mCarUxRManager.registerListener(listener);
// With no gear value available, driving state should be unknown
listener.reset();
// Test Parked state and corresponding restrictions based on car_ux_restrictions_map.xml
Log.d(TAG, "Injecting gear park");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(VehicleGear.GEAR_PARK)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED);
Log.d(TAG, "Injecting speed 0");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(0.0f)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
// Switch gear to drive. Driving state changes to Idling but the UX restrictions don't
// change between parked and idling.
listener.reset();
Log.d(TAG, "Injecting gear drive");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(VehicleGear.GEAR_DRIVE)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING);
// Test Moving state and corresponding restrictions based on car_ux_restrictions_map.xml
listener.reset();
Log.d(TAG, "Injecting speed 30");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(30.0f)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_MOVING);
restrictions = listener.waitForUxRestrictionsChange();
assertNotNull(restrictions);
assertTrue(restrictions.isRequiresDistractionOptimization());
assertThat(restrictions.getActiveRestrictions()).isEqualTo(UX_RESTRICTIONS_MOVING);
// Test Idling state and corresponding restrictions based on car_ux_restrictions_map.xml
listener.reset();
Log.d(TAG, "Injecting speed 0");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(0.0f)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_IDLING);
restrictions = listener.waitForUxRestrictionsChange();
assertNotNull(restrictions);
assertFalse(restrictions.isRequiresDistractionOptimization());
assertThat(restrictions.getActiveRestrictions())
.isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
// Apply Parking brake. Supported gears is not provided in this test and hence
// Automatic transmission should be assumed and hence parking brake state should not
// make a difference to the driving state.
listener.reset();
Log.d(TAG, "Injecting parking brake on");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON)
.setBooleanValue(true)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
assertNull(drivingEvent);
mCarDrivingStateManager.unregisterListener();
mCarUxRManager.unregisterListener();
}
@Test
public void testDrivingStateChangeForMalformedInputs() throws InterruptedException {
CarDrivingStateEvent drivingEvent;
CarUxRestrictions restrictions;
DrivingStateListener listener = new DrivingStateListener();
mCarDrivingStateManager.registerListener(listener);
mCarUxRManager.registerListener(listener);
// Start with gear = park and speed = 0 to begin with a known state.
listener.reset();
Log.d(TAG, "Injecting gear park");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(VehicleGear.GEAR_PARK)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(CarDrivingStateEvent.DRIVING_STATE_PARKED);
Log.d(TAG, "Injecting speed 0");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(0.0f)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
// Inject an invalid gear. Since speed is still valid, idling will be the expected
// driving state
listener.reset();
Log.d(TAG, "Injecting gear -1");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION)
.addIntValue(-1)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
if (Build.IS_DEBUGGABLE) {
// In userdebug build, payloadChecker in HAL drops the invalid event.
assertNull(drivingEvent);
} else {
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(
CarDrivingStateEvent.DRIVING_STATE_IDLING);
}
// Now, send in an invalid speed value as well, now the driving state will be unknown and
// the UX restrictions will change to fully restricted.
listener.reset();
Log.d(TAG, "Injecting speed -1");
getHidlMockedVehicleHal().injectEvent(
VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED)
.addFloatValue(-1.0f)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build());
drivingEvent = listener.waitForDrivingStateChange();
if (Build.IS_DEBUGGABLE) {
// In userdebug build, payloadChecker in HAL drops the invalid event.
assertNull(drivingEvent);
} else {
assertNotNull(drivingEvent);
assertThat(drivingEvent.eventValue).isEqualTo(
CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
restrictions = listener.waitForUxRestrictionsChange();
assertNotNull(restrictions);
assertTrue(restrictions.isRequiresDistractionOptimization());
assertThat(restrictions.getActiveRestrictions())
.isEqualTo(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED);
}
mCarDrivingStateManager.unregisterListener();
mCarUxRManager.unregisterListener();
}
/**
* Callback function we register for driving state update notifications.
*/
private class DrivingStateListener implements
CarDrivingStateManager.CarDrivingStateEventListener,
CarUxRestrictionsManager.OnUxRestrictionsChangedListener {
private final Object mDrivingStateLock = new Object();
@GuardedBy("mDrivingStateLock")
private CarDrivingStateEvent mLastEvent = null;
private final Object mUxRLock = new Object();
@GuardedBy("mUxRLock")
private CarUxRestrictions mLastRestrictions = null;
void reset() {
mLastEvent = null;
mLastRestrictions = null;
}
// Returns True to indicate receipt of a driving state event. False indicates a timeout.
CarDrivingStateEvent waitForDrivingStateChange() throws InterruptedException {
long start = SystemClock.elapsedRealtime();
synchronized (mDrivingStateLock) {
while (mLastEvent == null
&& (start + DEFAULT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) {
mDrivingStateLock.wait(100L);
}
return mLastEvent;
}
}
@Override
public void onDrivingStateChanged(CarDrivingStateEvent event) {
Log.d(TAG, "onDrivingStateChanged, event: " + event.eventValue);
synchronized (mDrivingStateLock) {
// We're going to hold a reference to this object
mLastEvent = event;
mDrivingStateLock.notify();
}
}
CarUxRestrictions waitForUxRestrictionsChange() throws InterruptedException {
long start = SystemClock.elapsedRealtime();
synchronized (mUxRLock) {
while (mLastRestrictions == null
&& (start + DEFAULT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) {
mUxRLock.wait(100L);
}
}
return mLastRestrictions;
}
@Override
public void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
Log.d(TAG, "onUxRestrictionsChanged, restrictions: "
+ restrictions.getActiveRestrictions());
synchronized (mUxRLock) {
mLastRestrictions = restrictions;
mUxRLock.notify();
}
}
}
}