1 /*
2  * Copyright (C) 2024 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 
17 package com.android.car.hal.property;
18 
19 import static com.android.car.CarServiceUtils.toIntArray;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 import static com.google.common.truth.Truth.assertWithMessage;
23 
24 import static org.junit.Assert.assertThrows;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.when;
27 
28 import android.car.Car;
29 import android.car.VehiclePropertyIds;
30 import android.car.feature.FakeFeatureFlagsImpl;
31 import android.car.feature.Flags;
32 import android.car.hardware.CarHvacFanDirection;
33 import android.car.test.AbstractExpectableTestCase;
34 import android.content.Context;
35 import android.content.pm.PackageManager;
36 import android.hardware.automotive.vehicle.VehicleGear;
37 import android.hardware.automotive.vehicle.VehicleProperty;
38 import android.hardware.automotive.vehicle.VehicleUnit;
39 import android.hardware.automotive.vehicle.VehicleVendorPermission;
40 import android.os.SystemClock;
41 import android.platform.test.ravenwood.RavenwoodRule;
42 import android.util.ArraySet;
43 import android.util.SparseArray;
44 
45 import com.android.car.hal.HalPropValue;
46 import com.android.car.hal.HalPropValueBuilder;
47 import com.android.car.hal.property.PropertyHalServiceConfigs.CarSvcPropertyConfig;
48 import com.android.car.hal.property.PropertyPermissionInfo.AllOfPermissions;
49 import com.android.car.hal.property.PropertyPermissionInfo.AnyOfPermissions;
50 import com.android.car.hal.property.PropertyPermissionInfo.PermissionCondition;
51 import com.android.car.hal.property.PropertyPermissionInfo.PropertyPermissions;
52 import com.android.car.hal.property.PropertyPermissionInfo.SinglePermission;
53 
54 import org.junit.Before;
55 import org.junit.Rule;
56 import org.junit.Test;
57 import org.mockito.Mock;
58 import org.mockito.MockitoAnnotations;
59 import org.mockito.junit.MockitoJUnit;
60 import org.mockito.junit.MockitoRule;
61 
62 import java.io.ByteArrayInputStream;
63 import java.io.IOException;
64 import java.io.InputStream;
65 import java.nio.charset.StandardCharsets;
66 import java.util.ArrayList;
67 import java.util.List;
68 import java.util.Set;
69 
70 public final class PropertyHalServiceConfigsUnitTest extends AbstractExpectableTestCase {
71     private static final int VENDOR_PROPERTY_1 = 0x21e01111;
72     private static final int VENDOR_PROPERTY_2 = 0x21e01112;
73     private static final int VENDOR_PROPERTY_3 = 0x21e01113;
74     private static final int VENDOR_PROPERTY_4 = 0x21e01114;
75     private static final int[] VENDOR_PROPERTY_IDS = {
76             VENDOR_PROPERTY_1, VENDOR_PROPERTY_2, VENDOR_PROPERTY_3, VENDOR_PROPERTY_4};
77     private static final int[] SYSTEM_PROPERTY_IDS = {VehiclePropertyIds.ENGINE_OIL_LEVEL,
78             VehiclePropertyIds.CURRENT_GEAR, VehiclePropertyIds.NIGHT_MODE,
79             VehiclePropertyIds.HVAC_FAN_SPEED, VehiclePropertyIds.DOOR_LOCK};
80     private static final int BACKPORTED_PROPERTY = 0x31e01111;
81 
82     @Rule
83     public MockitoRule mMockitoRule = MockitoJUnit.rule();
84 
85     @Rule
86     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
87 
88     @Mock
89     private Context mContext;
90 
91     private PropertyHalServiceConfigs mPropertyHalServiceConfigs;
92     private static final HalPropValueBuilder PROP_VALUE_BUILDER =
93             new HalPropValueBuilder(/*isAidl=*/true);
94     //payload test
95     private static final HalPropValue GEAR_WITH_VALID_VALUE =
96             PROP_VALUE_BUILDER.build(VehicleProperty.GEAR_SELECTION, /*areaId=*/0,
97                     /*timestamp=*/SystemClock.elapsedRealtimeNanos(), /*status=*/0,
98                     VehicleGear.GEAR_DRIVE);
99     private static final HalPropValue GEAR_WITH_EXTRA_VALUE =
100             PROP_VALUE_BUILDER.build(VehicleProperty.GEAR_SELECTION, /*areaId=*/0,
101                     /*timestamp=*/SystemClock.elapsedRealtimeNanos(), /*status=*/0,
102                     new int[]{VehicleGear.GEAR_DRIVE, VehicleGear.GEAR_1});
103     private static final HalPropValue GEAR_WITH_INVALID_VALUE =
104             PROP_VALUE_BUILDER.build(VehicleProperty.GEAR_SELECTION, /*areaId=*/0,
105                     /*timestamp=*/SystemClock.elapsedRealtimeNanos(), /*status=*/0,
106                     VehicleUnit.KILOPASCAL);
107     private static final HalPropValue GEAR_WITH_INVALID_TYPE_VALUE =
108             PROP_VALUE_BUILDER.build(VehicleProperty.GEAR_SELECTION, /*areaId=*/0,
109                     /*timestamp=*/SystemClock.elapsedRealtimeNanos(), /*status=*/0,
110                     1.0f);
111     private static final HalPropValue HVAC_FAN_DIRECTIONS_VALID =
112             PROP_VALUE_BUILDER.build(VehicleProperty.HVAC_FAN_DIRECTION, /*areaId=*/0,
113                     /*timestamp=*/SystemClock.elapsedRealtimeNanos(), /*status=*/0,
114                     CarHvacFanDirection.FACE | CarHvacFanDirection.FLOOR);
115     private static final HalPropValue HVAC_FAN_DIRECTIONS_INVALID =
116             PROP_VALUE_BUILDER.build(VehicleProperty.HVAC_FAN_DIRECTION, /*areaId=*/0,
117                     /*timestamp=*/SystemClock.elapsedRealtimeNanos(), /*status=*/0,
118                     CarHvacFanDirection.FACE | 0x100);
119     private static final HalPropValue ULTRASONCIS_SENSOR_MEASURED_DISTANCE_WITH_ERROR_VALID =
120             PROP_VALUE_BUILDER.build(
121                     /*prop=*/ VehiclePropertyIds.ULTRASONICS_SENSOR_MEASURED_DISTANCE,
122                     /*areaId=*/ 1,
123                     /*timestamp=*/ SystemClock.elapsedRealtimeNanos(),
124                     /*status=*/ 0,
125                     /*values=*/ new int[]{300, 10});
126     private static final HalPropValue ULTRASONCIS_SENSOR_MEASURED_DISTANCE_EMPTY_ARRAY_VALID =
127             PROP_VALUE_BUILDER.build(
128                     /*prop=*/ VehiclePropertyIds.ULTRASONICS_SENSOR_MEASURED_DISTANCE,
129                     /*areaId=*/ 1,
130                     /*timestamp=*/ SystemClock.elapsedRealtimeNanos(),
131                     /*status=*/ 0,
132                     /*values=*/ new int[]{});
133 
134     private FakeFeatureFlagsImpl mFakeFeatureFlags;
135 
136     @Before
setUp()137     public void setUp() {
138         MockitoAnnotations.initMocks(this);
139 
140         mFakeFeatureFlags = new FakeFeatureFlagsImpl();
141         mFakeFeatureFlags.setFlag(Flags.FLAG_ANDROID_VIC_VEHICLE_PROPERTIES, true);
142 
143         mPropertyHalServiceConfigs = new PropertyHalServiceConfigs(mFakeFeatureFlags);
144 
145         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CAR_ENGINE_DETAILED))
146                 .thenReturn(PackageManager.PERMISSION_GRANTED);
147         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE))
148                 .thenReturn(PackageManager.PERMISSION_GRANTED);
149         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_READ_WINDSHIELD_WIPERS))
150                 .thenReturn(PackageManager.PERMISSION_DENIED);
151         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_WINDSHIELD_WIPERS))
152                 .thenReturn(PackageManager.PERMISSION_GRANTED);
153         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_ADAS_STATES))
154                 .thenReturn(PackageManager.PERMISSION_DENIED);
155         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_READ_DISPLAY_UNITS))
156                 .thenReturn(PackageManager.PERMISSION_DENIED);
157         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_DISPLAY_UNITS))
158                 .thenReturn(PackageManager.PERMISSION_GRANTED);
159         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VENDOR_EXTENSION))
160                 .thenReturn(PackageManager.PERMISSION_GRANTED);
161     }
162 
163     /**
164      * Test {@link PropertyHalServiceConfigs#checkPayload(HalPropValue)}
165      */
166     @Test
testCheckPayload()167     public void testCheckPayload() {
168         assertThat(mPropertyHalServiceConfigs.checkPayload(GEAR_WITH_VALID_VALUE)).isTrue();
169         assertThat(mPropertyHalServiceConfigs.checkPayload(GEAR_WITH_EXTRA_VALUE)).isFalse();
170         assertThat(mPropertyHalServiceConfigs.checkPayload(GEAR_WITH_INVALID_VALUE)).isFalse();
171         assertThat(mPropertyHalServiceConfigs.checkPayload(GEAR_WITH_INVALID_TYPE_VALUE)).isFalse();
172         assertThat(mPropertyHalServiceConfigs.checkPayload(HVAC_FAN_DIRECTIONS_VALID)).isTrue();
173         assertThat(mPropertyHalServiceConfigs.checkPayload(HVAC_FAN_DIRECTIONS_INVALID)).isFalse();
174         assertThat(mPropertyHalServiceConfigs
175                 .checkPayload(ULTRASONCIS_SENSOR_MEASURED_DISTANCE_WITH_ERROR_VALID))
176                 .isTrue();
177         assertThat(mPropertyHalServiceConfigs
178                 .checkPayload(ULTRASONCIS_SENSOR_MEASURED_DISTANCE_EMPTY_ARRAY_VALID))
179                 .isTrue();
180     }
181 
182     /**
183      * Test {@link PropertyHalServiceConfigs#isReadable(Context, int)}
184      * and {@link PropertyHalServiceConfigs#isWritable(Context, int)} for system properties
185      */
186     @Test
testIsReadableWritableForSystemProperty()187     public void testIsReadableWritableForSystemProperty() {
188         assertThat(mPropertyHalServiceConfigs
189                 .isReadable(mContext, VehiclePropertyIds.ENGINE_OIL_LEVEL)).isTrue();
190         assertThat(mPropertyHalServiceConfigs
191                 .isWritable(mContext, VehiclePropertyIds.ENGINE_OIL_LEVEL)).isFalse();
192         assertThat(mPropertyHalServiceConfigs
193                 .isReadable(mContext, VehiclePropertyIds.HVAC_FAN_SPEED)).isTrue();
194         assertThat(mPropertyHalServiceConfigs
195                 .isWritable(mContext, VehiclePropertyIds.HVAC_FAN_SPEED)).isTrue();
196         assertThat(mPropertyHalServiceConfigs
197                 .isReadable(mContext, VehiclePropertyIds.WINDSHIELD_WIPERS_SWITCH)).isTrue();
198         assertThat(mPropertyHalServiceConfigs
199                 .isWritable(mContext, VehiclePropertyIds.WINDSHIELD_WIPERS_SWITCH)).isTrue();
200         assertThat(mPropertyHalServiceConfigs
201                 .isReadable(mContext, VehiclePropertyIds.CRUISE_CONTROL_COMMAND)).isFalse();
202         assertThat(mPropertyHalServiceConfigs
203                 .isWritable(mContext, VehiclePropertyIds.CRUISE_CONTROL_COMMAND)).isFalse();
204         assertThat(mPropertyHalServiceConfigs
205                 .isReadable(mContext, VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS)).isTrue();
206         assertThat(mPropertyHalServiceConfigs
207                 .isWritable(mContext, VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS)).isTrue();
208         assertThat(mPropertyHalServiceConfigs
209                 .isReadable(mContext, VehiclePropertyIds.DISTANCE_DISPLAY_UNITS)).isFalse();
210         assertThat(mPropertyHalServiceConfigs
211                 .isWritable(mContext, VehiclePropertyIds.DISTANCE_DISPLAY_UNITS)).isTrue();
212 
213         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE))
214                 .thenReturn(PackageManager.PERMISSION_DENIED);
215         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VENDOR_EXTENSION))
216                 .thenReturn(PackageManager.PERMISSION_DENIED);
217         assertThat(mPropertyHalServiceConfigs
218                 .isReadable(mContext, VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS)).isFalse();
219         assertThat(mPropertyHalServiceConfigs
220                 .isWritable(mContext, VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS)).isFalse();
221         assertThat(mPropertyHalServiceConfigs
222                 .isReadable(mContext, VehiclePropertyIds.DISTANCE_DISPLAY_UNITS)).isFalse();
223         assertThat(mPropertyHalServiceConfigs
224                 .isWritable(mContext, VehiclePropertyIds.DISTANCE_DISPLAY_UNITS)).isFalse();
225     }
226 
227     /**
228      * Test for READ-WRITE system properties that write permissions are present in read permissions.
229      */
230     @Test
testWritePermissionsExistInReadPermissionsForSystemProperty()231     public void testWritePermissionsExistInReadPermissionsForSystemProperty() {
232         List<Integer> allSystemHalPropIds = mPropertyHalServiceConfigs.getAllSystemHalPropIds();
233         for (int halPropId : allSystemHalPropIds) {
234             PermissionCondition readPermission =
235                     mPropertyHalServiceConfigs.getReadPermission(halPropId);
236             if (readPermission == null) {
237                 continue;
238             }
239             PermissionCondition writePermission =
240                     mPropertyHalServiceConfigs.getWritePermission(halPropId);
241 
242             // This skips the _DISPLAY_UNITS properties, which have multiple write permissions.
243             if (!(writePermission instanceof SinglePermission)) {
244                 continue;
245             }
246 
247             if (readPermission.equals(writePermission)) {
248                 continue;
249             }
250 
251             assertWithMessage("Read permission " + readPermission + " of property "
252                     + VehiclePropertyIds.toString(halPropId)
253                     + " should be an instance of AnyOfPermissions if not equal to write permission")
254                     .that(readPermission instanceof AnyOfPermissions).isTrue();
255             assertWithMessage("Read permission " + readPermission + " of property "
256                     + VehiclePropertyIds.toString(halPropId)
257                     + " should be equal to or contain write permission " + writePermission)
258                     .that(((AnyOfPermissions) readPermission)
259                             .isMetIfGranted((SinglePermission) writePermission)).isTrue();
260         }
261     }
262 
263     /**
264      * Test {@link PropertyHalServiceConfigs#isReadable(Context, int)}
265      * and {@link PropertyHalServiceConfigs#isWritable(Context, int)} for vendor properties
266      */
267     @Test
testIsReadableWritableForVendorProperty()268     public void testIsReadableWritableForVendorProperty() {
269         for (int vendorProp : VENDOR_PROPERTY_IDS) {
270             assertThat(mPropertyHalServiceConfigs.isReadable(mContext, vendorProp)).isTrue();
271             assertThat(mPropertyHalServiceConfigs.isWritable(mContext, vendorProp)).isTrue();
272         }
273 
274         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VENDOR_EXTENSION))
275                 .thenReturn(PackageManager.PERMISSION_DENIED);
276         for (int vendorProp : VENDOR_PROPERTY_IDS) {
277             assertThat(mPropertyHalServiceConfigs.isReadable(mContext, vendorProp)).isFalse();
278             assertThat(mPropertyHalServiceConfigs.isWritable(mContext, vendorProp)).isFalse();
279         }
280     }
281 
282     /**
283      * Test {@link PropertyHalServiceConfigs#isSupportedProperty}
284      */
285     @Test
testIsSupportedProperty()286     public void testIsSupportedProperty() {
287         for (int vendorProp : VENDOR_PROPERTY_IDS) {
288             expectWithMessage("vendor property: " + vendorProp + " is supported").that(
289                     mPropertyHalServiceConfigs.isSupportedProperty(vendorProp)).isTrue();
290         }
291         for (int systemProp : SYSTEM_PROPERTY_IDS) {
292             expectWithMessage("system property: " + VehiclePropertyIds.toString(systemProp)
293                     + " is supported").that(
294                     mPropertyHalServiceConfigs.isSupportedProperty(systemProp)).isTrue();
295         }
296 
297         int fakeSystemPropId = 0x1fffffff;
298         expectWithMessage("fake system property: " + fakeSystemPropId + " is not supported").that(
299                 mPropertyHalServiceConfigs.isSupportedProperty(fakeSystemPropId)).isFalse();
300         int fakePropId = 0xffffffff;
301         expectWithMessage("fake property: " + fakePropId + " is not supported").that(
302                 mPropertyHalServiceConfigs.isSupportedProperty(fakePropId)).isFalse();
303 
304         expectWithMessage("backported property: " + BACKPORTED_PROPERTY + " is supported").that(
305                 mPropertyHalServiceConfigs.isSupportedProperty(BACKPORTED_PROPERTY)).isTrue();
306     }
307 
308     /**
309      * Test {@link PropertyHalServiceConfigs#customizeVendorPermission(int[])}
310      */
311     @Test
testCustomizeVendorPermission()312     public void testCustomizeVendorPermission() {
313         List<Integer> configArray = new ArrayList<>();
314         // set up read permission and write permission to VENDOR_PROPERTY_1
315         configArray.add(VENDOR_PROPERTY_1);
316         configArray.add(VehicleVendorPermission.PERMISSION_DEFAULT);
317         configArray.add(VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE);
318         // set up read permission and write permission to VENDOR_PROPERTY_2
319         configArray.add(VENDOR_PROPERTY_2);
320         configArray.add(VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_ENGINE);
321         configArray.add(VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_ENGINE);
322         // set up read permission and write permission to VENDOR_PROPERTY_3
323         configArray.add(VENDOR_PROPERTY_3);
324         configArray.add(VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_INFO);
325         configArray.add(VehicleVendorPermission.PERMISSION_DEFAULT);
326         // set up read permission and write permission to BACKPORTED_PROPERTY
327         configArray.add(BACKPORTED_PROPERTY);
328         configArray.add(VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_INFO);
329         configArray.add(VehicleVendorPermission.PERMISSION_DEFAULT);
330 
331         mPropertyHalServiceConfigs.customizeVendorPermission(toIntArray(configArray));
332 
333         assertThat(mPropertyHalServiceConfigs.getReadPermission(VENDOR_PROPERTY_1)
334                 .toString()).isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
335         assertThat(mPropertyHalServiceConfigs.getWritePermission(VENDOR_PROPERTY_1)).isNull();
336 
337         assertThat(mPropertyHalServiceConfigs.getReadPermission(VENDOR_PROPERTY_2)
338                 .toString()).isEqualTo(android.car.hardware.property
339                 .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE);
340         assertThat(mPropertyHalServiceConfigs.getWritePermission(VENDOR_PROPERTY_2)
341                 .toString()).isEqualTo(android.car.hardware.property
342                 .VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE);
343 
344         assertThat(mPropertyHalServiceConfigs.getReadPermission(VENDOR_PROPERTY_3)
345                 .toString()).isEqualTo(android.car.hardware.property
346                 .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
347         assertThat(mPropertyHalServiceConfigs.getWritePermission(VENDOR_PROPERTY_3)
348                 .toString()).isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
349 
350         assertThat(mPropertyHalServiceConfigs.getReadPermission(VENDOR_PROPERTY_4)
351                 .toString()).isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
352         assertThat(mPropertyHalServiceConfigs.getWritePermission(VENDOR_PROPERTY_4)
353                 .toString()).isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
354 
355         assertThat(mPropertyHalServiceConfigs.getReadPermission(BACKPORTED_PROPERTY)
356                 .toString()).isEqualTo(android.car.hardware.property
357                 .VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
358         assertThat(mPropertyHalServiceConfigs.getWritePermission(BACKPORTED_PROPERTY)
359                 .toString()).isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
360 
361         when(mContext.checkCallingOrSelfPermission(Car.PERMISSION_VENDOR_EXTENSION))
362                 .thenReturn(PackageManager.PERMISSION_GRANTED);
363         when(mContext.checkCallingOrSelfPermission(
364                 android.car.hardware.property.VehicleVendorPermission
365                         .PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE))
366                 .thenReturn(PackageManager.PERMISSION_GRANTED);
367         when(mContext.checkCallingOrSelfPermission(
368                 android.car.hardware.property.VehicleVendorPermission
369                         .PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE))
370                 .thenReturn(PackageManager.PERMISSION_GRANTED);
371         when(mContext.checkCallingOrSelfPermission(
372                 android.car.hardware.property.VehicleVendorPermission
373                         .PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO))
374                 .thenReturn(PackageManager.PERMISSION_DENIED);
375 
376         assertThat(mPropertyHalServiceConfigs.isReadable(mContext, VENDOR_PROPERTY_1)).isTrue();
377         assertThat(mPropertyHalServiceConfigs.isWritable(mContext, VENDOR_PROPERTY_1)).isFalse();
378         assertThat(mPropertyHalServiceConfigs.isReadable(mContext, VENDOR_PROPERTY_2)).isTrue();
379         assertThat(mPropertyHalServiceConfigs.isWritable(mContext, VENDOR_PROPERTY_2)).isTrue();
380         assertThat(mPropertyHalServiceConfigs.isReadable(mContext, VENDOR_PROPERTY_3)).isFalse();
381         assertThat(mPropertyHalServiceConfigs.isWritable(mContext, VENDOR_PROPERTY_3)).isTrue();
382         assertThat(mPropertyHalServiceConfigs.isReadable(mContext, VENDOR_PROPERTY_4)).isTrue();
383         assertThat(mPropertyHalServiceConfigs.isWritable(mContext, VENDOR_PROPERTY_4)).isTrue();
384         assertThat(mPropertyHalServiceConfigs.isReadable(mContext, BACKPORTED_PROPERTY)).isFalse();
385         assertThat(mPropertyHalServiceConfigs.isWritable(mContext, BACKPORTED_PROPERTY)).isTrue();
386     }
387 
388     @Test
testCustomizeVendorPermission_systemPropertyNotAllowed()389     public void testCustomizeVendorPermission_systemPropertyNotAllowed() {
390         List<Integer> configArray = new ArrayList<>();
391         configArray.add(VehiclePropertyIds.CURRENT_GEAR);
392         configArray.add(
393                 VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_ENGINE);
394         configArray.add(
395                 VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_ENGINE);
396 
397         assertThrows(IllegalArgumentException.class, () ->
398                 mPropertyHalServiceConfigs.customizeVendorPermission(toIntArray(configArray)));
399     }
400 
401     @Test
testCustomizeVendorPermission_overrideNotAllowed()402     public void testCustomizeVendorPermission_overrideNotAllowed() {
403         List<Integer> configArray = new ArrayList<>();
404         configArray.add(VENDOR_PROPERTY_1);
405         configArray.add(VehicleVendorPermission.PERMISSION_DEFAULT);
406         configArray.add(VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE);
407         // This must not take effect.
408         configArray.add(VENDOR_PROPERTY_1);
409         configArray.add(VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_ENGINE);
410         configArray.add(VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_ENGINE);
411 
412         mPropertyHalServiceConfigs.customizeVendorPermission(toIntArray(configArray));
413 
414         assertThat(mPropertyHalServiceConfigs.getReadPermission(VENDOR_PROPERTY_1)
415                 .toString()).isEqualTo(Car.PERMISSION_VENDOR_EXTENSION);
416         assertThat(mPropertyHalServiceConfigs.getWritePermission(VENDOR_PROPERTY_1)).isNull();
417     }
418 
strToInputStream(String str)419     private InputStream strToInputStream(String str) {
420         return new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
421     }
422 
423     @Test
testParseJsonConfig_validConfig()424     public void testParseJsonConfig_validConfig() throws Exception {
425         InputStream is = strToInputStream("""
426         {
427             'version': 1,
428             'properties': {
429                 'PROP_NAME': {
430                     'propertyName': 'PROP_NAME',
431                     'propertyId': 1234,
432                     'vhalPropertyId': 2345,
433                     'description': 'DESCRIPTION',
434                     'readPermission': {
435                         'type': 'anyOf',
436                         'value': [
437                             {
438                                 'type': 'single',
439                                 'value': 'PERM1'
440                             },
441                             {
442                                 'type': 'single',
443                                 'value': 'PERM2'
444                             }
445                         ]
446                     },
447                     'writePermission': {
448                         'type': 'allOf',
449                         'value': [
450                             {
451                                 'type': 'single',
452                                 'value': 'PERM1'
453                             },
454                             {
455                                 'type': 'single',
456                                 'value': 'PERM2'
457                             }
458                         ]
459                     },
460                     'dataEnums': [0, 1, 2]
461                 }
462             }
463         }
464                 """);
465         CarSvcPropertyConfig expectedConfig = new CarSvcPropertyConfig();
466         expectedConfig.propertyId = 1234;
467         expectedConfig.halPropId = 2345;
468         expectedConfig.propertyName = "PROP_NAME";
469         expectedConfig.description = "DESCRIPTION";
470         expectedConfig.permissions =  new PropertyPermissions.Builder()
471                 .setReadPermission(new AnyOfPermissions(
472                         new SinglePermission("PERM1"),
473                         new SinglePermission("PERM2")
474                 ))
475                 .setWritePermission(new AllOfPermissions(
476                         new SinglePermission("PERM1"),
477                         new SinglePermission("PERM2")
478                 ))
479                 .build();
480         expectedConfig.dataEnums = new ArraySet<>(Set.of(0, 1, 2));
481 
482         SparseArray<CarSvcPropertyConfig> configs = mPropertyHalServiceConfigs.parseJsonConfig(
483                 is, /* path= */ "test");
484 
485         assertWithMessage("expect one config parsed").that(configs.size()).isEqualTo(1);
486         CarSvcPropertyConfig config = configs.get(2345);
487         assertThat(config).isNotNull();
488         assertThat(config).isEqualTo(expectedConfig);
489     }
490 
491     @Test
testParseJsonConfig_halPropIdDefaultEqualPropId()492     public void testParseJsonConfig_halPropIdDefaultEqualPropId() throws Exception {
493         InputStream is = strToInputStream("""
494         {
495             "version": 1,
496             "properties": {
497                 "PROP_NAME": {
498                     "propertyName": "PROP_NAME",
499                     "propertyId": 1234,
500                     "description": "DESCRIPTION",
501                     "readPermission": {
502                         "type": "single",
503                         "value": "PERM1"
504                     }
505                 }
506             }
507         }
508                 """);
509         CarSvcPropertyConfig expectedConfig = new CarSvcPropertyConfig();
510         expectedConfig.propertyId = 1234;
511         expectedConfig.halPropId = 1234;
512         expectedConfig.propertyName = "PROP_NAME";
513         expectedConfig.description = "DESCRIPTION";
514         expectedConfig.permissions =  new PropertyPermissions.Builder()
515                 .setReadPermission(new SinglePermission("PERM1"))
516                 .build();
517 
518         SparseArray<CarSvcPropertyConfig> configs = mPropertyHalServiceConfigs.parseJsonConfig(
519                 is, /* path= */ "test");
520         CarSvcPropertyConfig config = configs.get(1234);
521         assertThat(config).isNotNull();
522         assertThat(config).isEqualTo(expectedConfig);
523     }
524 
525     @Test
testParseJsonConfig_dataFlags()526     public void testParseJsonConfig_dataFlags() throws Exception {
527         InputStream is = strToInputStream("""
528         {
529             "version": 1,
530             "properties": {
531                 "PROP_NAME": {
532                     "propertyName": "PROP_NAME",
533                     "propertyId": 1234,
534                     "description": "DESCRIPTION",
535                     "readPermission": {
536                         "type": "single",
537                         "value": "PERM1"
538                     },
539                     "dataFlags": [1, 2, 4, 8]
540                 }
541             }
542         }
543                 """);
544         CarSvcPropertyConfig expectedConfig = new CarSvcPropertyConfig();
545         expectedConfig.propertyId = 1234;
546         expectedConfig.halPropId = 1234;
547         expectedConfig.propertyName = "PROP_NAME";
548         expectedConfig.description = "DESCRIPTION";
549         expectedConfig.permissions =  new PropertyPermissions.Builder()
550                 .setReadPermission(new SinglePermission("PERM1"))
551                 .build();
552         // Bit: 1111
553         expectedConfig.validBitFlag = 15;
554 
555         SparseArray<CarSvcPropertyConfig> configs = mPropertyHalServiceConfigs.parseJsonConfig(
556                 is, /* path= */ "test");
557         CarSvcPropertyConfig config = configs.get(1234);
558         assertThat(config).isNotNull();
559         assertThat(config).isEqualTo(expectedConfig);
560     }
561 
562     @Test
testParseJsonConfig_ignoreDeprecate()563     public void testParseJsonConfig_ignoreDeprecate() throws Exception {
564         InputStream is = strToInputStream("""
565         {
566             "version": 1,
567             "properties": {
568                 "OLD_PROP": {
569                     "propertyName": "OLD_PROP",
570                     "propertyId": 12345,
571                     "deprecated": true
572                 },
573                 "PROP_NAME": {
574                     "propertyName": "PROP_NAME",
575                     "propertyId": 1234,
576                     "vhalPropertyId": 2345,
577                     "description": "DESCRIPTION",
578                     "readPermission": {
579                         "type": "single",
580                         "value": "PERM1"
581                     }
582                 }
583             }
584         }
585                 """);
586 
587         SparseArray<CarSvcPropertyConfig> configs = mPropertyHalServiceConfigs.parseJsonConfig(
588                 is, /* path= */ "test");
589 
590         assertWithMessage("deprecated property must be ignored").that(configs.get(12345)).isNull();
591         assertWithMessage("must continue parsing non deprecated property").that(configs.get(2345))
592                 .isNotNull();
593     }
594 
595     @Test
testParseJsonConfig_cannotReadFromStream()596     public void testParseJsonConfig_cannotReadFromStream() throws Exception {
597         InputStream is = mock(InputStream.class);
598         when(is.readAllBytes()).thenThrow(new IOException());
599 
600         assertThrows(IllegalArgumentException.class, () ->
601                 mPropertyHalServiceConfigs.parseJsonConfig(is, /* path= */ "test"));
602     }
603 
604     @Test
testParseJsonConfig_invalidJsonFormat()605     public void testParseJsonConfig_invalidJsonFormat() throws Exception {
606         InputStream is = strToInputStream("{");
607 
608         assertThrows(IllegalArgumentException.class, () ->
609                 mPropertyHalServiceConfigs.parseJsonConfig(is, /* path= */ "test"));
610     }
611 
612     @Test
testParseJsonConfig_missingPropertyIdField()613     public void testParseJsonConfig_missingPropertyIdField() throws Exception {
614         InputStream is = strToInputStream("""
615         {
616             "version": 1,
617             "properties": {
618                 "PROP_NAME": {
619                     "propertyName": "PROP_NAME",
620                     "vhalPropertyId": 2345,
621                     "description": "DESCRIPTION",
622                     "readPermission": {
623                         "type": "single",
624                         "value": "PERM1"
625                     }
626                 }
627             }
628         }
629                 """);
630 
631         assertThrows(IllegalArgumentException.class, () ->
632                 mPropertyHalServiceConfigs.parseJsonConfig(is, /* path= */ "test"));
633     }
634 
635     @Test
testParseJsonConfig_noReadOrWritePermission()636     public void testParseJsonConfig_noReadOrWritePermission() throws Exception {
637         InputStream is = strToInputStream("""
638         {
639             "version": 1,
640             "properties": {
641                 "PROP_NAME": {
642                     "propertyName": "PROP_NAME",
643                     "propertyId": 1234,
644                     "description": "DESCRIPTION",
645                     "dataFlags": [1, 2, 4, 8]
646                 }
647             }
648         }
649                 """);
650 
651         assertThrows(IllegalArgumentException.class, () ->
652                 mPropertyHalServiceConfigs.parseJsonConfig(is, /* path= */ "test"));
653     }
654 
655     @Test
testManagerToHalPropId()656     public void testManagerToHalPropId() {
657         assertThat(mPropertyHalServiceConfigs.managerToHalPropId(
658                 VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS)).isEqualTo(
659                 VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
660     }
661 
662     @Test
testHalToManagerPropId()663     public void testHalToManagerPropId() {
664         assertThat(mPropertyHalServiceConfigs.halToManagerPropId(
665                 VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS)).isEqualTo(
666                 VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS);
667     }
668 
669 
670     @Test
testVicVehiclePropertiesFlagDisabled()671     public void testVicVehiclePropertiesFlagDisabled() throws Exception {
672         int vicProperty = VehiclePropertyIds.DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED;
673         mFakeFeatureFlags.setFlag(Flags.FLAG_ANDROID_VIC_VEHICLE_PROPERTIES, false);
674 
675         mPropertyHalServiceConfigs = new PropertyHalServiceConfigs(mFakeFeatureFlags);
676 
677         assertThat(mPropertyHalServiceConfigs.getAllSystemHalPropIds()).doesNotContain(vicProperty);
678         assertThat(mPropertyHalServiceConfigs.isSupportedProperty(vicProperty)).isFalse();
679     }
680 }
681