blob: aadb6f946224f21f29c840dcfdc12c03e6c19a69 [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.android.car.CarUxRestrictionsManagerService.CONFIG_FILENAME_PRODUCTION;
import static com.android.car.CarUxRestrictionsManagerService.CONFIG_FILENAME_STAGED;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsConfiguration;
import android.car.drivingstate.CarUxRestrictionsConfiguration.Builder;
import android.car.hardware.CarPropertyValue;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.SystemClock;
import android.util.JsonReader;
import android.util.JsonWriter;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.car.systeminterface.SystemInterface;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CarUxRestrictionsManagerServiceTest {
private CarUxRestrictionsManagerService mService;
@Mock
private CarDrivingStateService mMockDrivingStateService;
@Mock
private CarPropertyService mMockCarPropertyService;
@Mock
private SystemInterface mMockSystemInterface;
private Context mSpyContext;
private File mTempSystemCarDir;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
// Spy context because service needs to access xml resource during init.
mSpyContext = spy(InstrumentationRegistry.getTargetContext());
CarLocalServices.removeServiceForTest(SystemInterface.class);
CarLocalServices.addService(SystemInterface.class, mMockSystemInterface);
mTempSystemCarDir = Files.createTempDirectory("uxr_test").toFile();
when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempSystemCarDir);
setUpMockParkedState();
mService = new CarUxRestrictionsManagerService(mSpyContext,
mMockDrivingStateService, mMockCarPropertyService);
}
@After
public void tearDown() throws Exception {
mService = null;
CarLocalServices.removeAllServices();
}
@Test
public void testSaveConfig_WriteStagedFile() throws Exception {
File staged = setupMockFile(CONFIG_FILENAME_STAGED, null);
CarUxRestrictionsConfiguration config = createEmptyConfig();
assertTrue(mService.saveUxRestrictionsConfigurationForNextBoot(Arrays.asList(config)));
assertTrue(readFile(staged).equals(config));
// Verify prod config file was not created.
assertFalse(new File(mTempSystemCarDir, CONFIG_FILENAME_PRODUCTION).exists());
}
@Test
public void testSaveConfig_ReturnFalseOnException() throws Exception {
CarUxRestrictionsConfiguration spyConfig = spy(createEmptyConfig());
doThrow(new IOException()).when(spyConfig).writeJson(any(JsonWriter.class));
assertFalse(mService.saveUxRestrictionsConfigurationForNextBoot(Arrays.asList(spyConfig)));
}
@Test
public void testLoadConfig_UseDefaultConfigWhenNoSavedConfigFileNoXml() {
// Prevent R.xml.car_ux_restrictions_map being returned.
Resources spyResources = spy(mSpyContext.getResources());
doReturn(spyResources).when(mSpyContext).getResources();
doReturn(null).when(spyResources).getXml(anyInt());
for (CarUxRestrictionsConfiguration config : mService.loadConfig()) {
assertTrue(config.equals(mService.createDefaultConfig(config.getPhysicalPort())));
}
}
@Test
public void testLoadConfig_UseXml() throws IOException, XmlPullParserException {
List<CarUxRestrictionsConfiguration> expected =
CarUxRestrictionsConfigurationXmlParser.parse(
mSpyContext, R.xml.car_ux_restrictions_map);
List<CarUxRestrictionsConfiguration> actual = mService.loadConfig();
assertTrue(actual.equals(expected));
}
@Test
public void testLoadConfig_UseProdConfig() throws IOException {
CarUxRestrictionsConfiguration expected = createEmptyConfig();
setupMockFile(CONFIG_FILENAME_PRODUCTION, expected);
CarUxRestrictionsConfiguration actual = mService.loadConfig().get(0);
assertTrue(actual.equals(expected));
}
@Test
public void testLoadConfig_PromoteStagedFileWhenParked() throws Exception {
CarUxRestrictionsConfiguration expected = createEmptyConfig();
// Staged file contains actual config. Ignore prod since it should be overwritten by staged.
File staged = setupMockFile(CONFIG_FILENAME_STAGED, expected);
// Set up temp file for prod to avoid polluting other tests.
setupMockFile(CONFIG_FILENAME_PRODUCTION, null);
CarUxRestrictionsConfiguration actual = mService.loadConfig().get(0);
// Staged file should be moved as production.
assertFalse(staged.exists());
assertTrue(actual.equals(expected));
}
@Test
public void testLoadConfig_NoPromoteStagedFileWhenMoving() throws Exception {
CarUxRestrictionsConfiguration expected = createEmptyConfig();
File staged = setupMockFile(CONFIG_FILENAME_STAGED, null);
// Prod file contains actual config. Ignore staged since it should not be promoted.
setupMockFile(CONFIG_FILENAME_PRODUCTION, expected);
setUpMockDrivingState();
CarUxRestrictionsConfiguration actual = mService.loadConfig().get(0);
// Staged file should be untouched.
assertTrue(staged.exists());
assertTrue(actual.equals(expected));
}
@Test
public void testValidateConfigs_SingleConfigNotSetPort() throws Exception {
CarUxRestrictionsConfiguration config = createEmptyConfig();
mService.validateConfigs(Arrays.asList(config));
}
@Test
public void testValidateConfigs_SingleConfigWithPort() throws Exception {
CarUxRestrictionsConfiguration config = createEmptyConfig((byte) 0);
mService.validateConfigs(Arrays.asList(config));
}
@Test(expected = IllegalArgumentException.class)
public void testValidateConfigs_MultipleConfigsMustHavePort() throws Exception {
mService.validateConfigs(Arrays.asList(createEmptyConfig(null), createEmptyConfig(null)));
}
@Test(expected = IllegalArgumentException.class)
public void testValidateConfigs_MultipleConfigsMustHaveUniquePort() throws Exception {
mService.validateConfigs(Arrays.asList(
createEmptyConfig((byte) 0), createEmptyConfig((byte) 0)));
}
@Test
public void testGetCurrentUxRestrictions_UnknownDisplayId_ReturnsFullRestrictions()
throws Exception {
mService.init();
CarUxRestrictions restrictions = mService.getCurrentUxRestrictions(/* displayId= */ 10);
CarUxRestrictions expected = new CarUxRestrictions.Builder(
/*reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED,
SystemClock.elapsedRealtimeNanos()).build();
assertTrue(restrictions.toString(), expected.isSameRestrictions(restrictions));
}
private CarUxRestrictionsConfiguration createEmptyConfig() {
return createEmptyConfig(null);
}
private CarUxRestrictionsConfiguration createEmptyConfig(Byte port) {
Builder builder = new Builder();
if (port != null) {
builder.setPhysicalPort(port);
}
return builder.build();
}
private void setUpMockParkedState() {
when(mMockDrivingStateService.getCurrentDrivingState()).thenReturn(
new CarDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_PARKED, 0));
CarPropertyValue<Float> speed = new CarPropertyValue<>(VehicleProperty.PERF_VEHICLE_SPEED,
0, 0f);
when(mMockCarPropertyService.getProperty(VehicleProperty.PERF_VEHICLE_SPEED, 0))
.thenReturn(speed);
}
private void setUpMockDrivingState() {
when(mMockDrivingStateService.getCurrentDrivingState()).thenReturn(
new CarDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_MOVING, 0));
CarPropertyValue<Float> speed = new CarPropertyValue<>(VehicleProperty.PERF_VEHICLE_SPEED,
0, 30f);
when(mMockCarPropertyService.getProperty(VehicleProperty.PERF_VEHICLE_SPEED, 0))
.thenReturn(speed);
}
private File setupMockFile(String filename, CarUxRestrictionsConfiguration config)
throws IOException {
File f = new File(mTempSystemCarDir, filename);
assertTrue(f.createNewFile());
if (config != null) {
try (JsonWriter writer = new JsonWriter(
new OutputStreamWriter(new FileOutputStream(f), "UTF-8"))) {
writer.beginArray();
config.writeJson(writer);
writer.endArray();
}
}
return f;
}
private CarUxRestrictionsConfiguration readFile(File file) throws Exception {
try (JsonReader reader = new JsonReader(
new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
CarUxRestrictionsConfiguration config = null;
reader.beginArray();
config = CarUxRestrictionsConfiguration.readJson(reader);
reader.endArray();
return config;
}
}
}