1 /* 2 * Copyright (C) 2023 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 android.car.Car.PERMISSION_VENDOR_EXTENSION; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.Nullable; 24 import android.car.VehiclePropertyIds; 25 import android.car.builtin.os.TraceHelper; 26 import android.car.builtin.util.Slogf; 27 import android.car.feature.FeatureFlags; 28 import android.car.feature.FeatureFlagsImpl; 29 import android.content.Context; 30 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 31 import android.hardware.automotive.vehicle.VehiclePropertyType; 32 import android.os.Trace; 33 import android.util.ArraySet; 34 import android.util.JsonReader; 35 import android.util.SparseArray; 36 37 import com.android.car.CarLog; 38 import com.android.car.hal.BidirectionalSparseIntArray; 39 import com.android.car.hal.HalPropValue; 40 import com.android.car.hal.property.PropertyPermissionInfo.AllOfPermissions; 41 import com.android.car.hal.property.PropertyPermissionInfo.AnyOfPermissions; 42 import com.android.car.hal.property.PropertyPermissionInfo.PermissionCondition; 43 import com.android.car.hal.property.PropertyPermissionInfo.PropertyPermissions; 44 import com.android.car.hal.property.PropertyPermissionInfo.PropertyPermissionsBuilder; 45 import com.android.car.hal.property.PropertyPermissionInfo.SinglePermission; 46 import com.android.car.internal.property.CarPropertyHelper; 47 import com.android.internal.annotations.GuardedBy; 48 import com.android.internal.annotations.VisibleForTesting; 49 50 import java.io.IOException; 51 import java.io.InputStream; 52 import java.io.InputStreamReader; 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.List; 56 import java.util.Set; 57 58 /** 59 * A singleton class to define which AIDL HAL property IDs are used by PropertyHalService. 60 * This class binds the read and write permissions to the property ID. 61 */ 62 public class PropertyHalServiceConfigs { 63 private static final long TRACE_TAG = TraceHelper.TRACE_TAG_CAR_SERVICE; 64 private static final Object sLock = new Object(); 65 @GuardedBy("sLock") 66 private static PropertyHalServiceConfigs sPropertyHalServiceConfigs; 67 private static final PermissionCondition SINGLE_PERMISSION_VENDOR_EXTENSION = 68 new SinglePermission(PERMISSION_VENDOR_EXTENSION); 69 /** 70 * Represents one VHAL property that is exposed through 71 * {@link android.car.hardware.property.CarPropertyManager}. 72 * 73 * Note that the property ID is defined in {@link android.car.VehiclePropertyIds} and it might 74 * be different than the property ID used by VHAL, defined in {@link VehicleProperty}. 75 * The latter is represented by {@code halPropId}. 76 * 77 * If the property is an {@code Integer} enum property, its supported enum values are listed 78 * in {@code dataEnums}. If the property is a flag-combination property, its valid bit flag is 79 * listed in {@code validBitFlag}. 80 */ 81 @VisibleForTesting CarSvcPropertyConfig( int propertyId, int halPropId, String propertyName, String description, PropertyPermissions permissions, @Nullable Set<Integer> dataEnums, @Nullable Integer validBitFlag)82 /* package */ record CarSvcPropertyConfig( 83 int propertyId, int halPropId, String propertyName, String description, 84 PropertyPermissions permissions, @Nullable Set<Integer> dataEnums, 85 @Nullable Integer validBitFlag) { 86 public CarSvcPropertyConfig { 87 requireNonNull(propertyName); 88 requireNonNull(description); 89 requireNonNull(permissions); 90 } 91 } 92 93 private static final String CONFIG_RESOURCE_NAME = "CarSvcProps.json"; 94 private static final String JSON_FIELD_NAME_PROPERTIES = "properties"; 95 96 private static final String VIC_FLAG_NAME = "FLAG_ANDROID_VIC_VEHICLE_PROPERTIES"; 97 private static final String REMOVE_SYSTEM_API_TAGS_FLAG_NAME = 98 "FLAG_VEHICLE_PROPERTY_REMOVE_SYSTEM_API_TAGS"; 99 private static final String FLAG_25Q2_3P_PERMISSIONS = 100 "FLAG_VEHICLE_PROPERTY_25Q2_3P_PERMISSIONS"; 101 private static final String B_FLAG_NAME = "FLAG_ANDROID_B_VEHICLE_PROPERTIES"; 102 103 private final FeatureFlags mFeatureFlags; 104 105 private static final String TAG = CarLog.tagFor(PropertyHalServiceConfigs.class); 106 107 private final SparseArray<Set<Integer>> mHalPropIdToEnumSet = new SparseArray<>(); 108 private final SparseArray<CarSvcPropertyConfig> mHalPropIdToCarSvcConfig; 109 private final BidirectionalSparseIntArray mMgrPropIdToHalPropId; 110 111 private final Object mLock = new Object(); 112 @GuardedBy("mLock") 113 private final SparseArray<PropertyPermissions> mVendorHalPropIdToPermissions = 114 new SparseArray<>(); 115 116 /** 117 * Should only be used in unit tests. Use {@link getInsance} instead. 118 */ 119 @VisibleForTesting PropertyHalServiceConfigs(@ullable FeatureFlags featureFlags)120 public PropertyHalServiceConfigs(@Nullable FeatureFlags featureFlags) { 121 Trace.traceBegin(TRACE_TAG, "initialize PropertyHalServiceConfigs"); 122 if (featureFlags == null) { 123 mFeatureFlags = new FeatureFlagsImpl(); 124 } else { 125 mFeatureFlags = featureFlags; 126 } 127 try (InputStream defaultConfigInputStream = this.getClass().getClassLoader() 128 .getResourceAsStream(CONFIG_RESOURCE_NAME)) { 129 mHalPropIdToCarSvcConfig = parseJsonConfig(defaultConfigInputStream, 130 "defaultResource"); 131 } catch (IOException e) { 132 String errorMsg = "failed to open/close resource input stream for: " 133 + CONFIG_RESOURCE_NAME; 134 Slogf.e(TAG, errorMsg, e); 135 throw new IllegalStateException(errorMsg, e); 136 } 137 List<Integer> halPropIdMgrIds = new ArrayList<>(); 138 for (int i = 0; i < mHalPropIdToCarSvcConfig.size(); i++) { 139 CarSvcPropertyConfig config = mHalPropIdToCarSvcConfig.valueAt(i); 140 if (config.halPropId() != config.propertyId()) { 141 halPropIdMgrIds.add(config.propertyId()); 142 halPropIdMgrIds.add(config.halPropId()); 143 } 144 } 145 int[] halPropIdMgrIdArray = new int[halPropIdMgrIds.size()]; 146 for (int i = 0; i < halPropIdMgrIds.size(); i++) { 147 halPropIdMgrIdArray[i] = halPropIdMgrIds.get(i); 148 } 149 mMgrPropIdToHalPropId = BidirectionalSparseIntArray.create(halPropIdMgrIdArray); 150 Trace.traceEnd(TRACE_TAG); 151 } 152 153 /** 154 * Gets the hard-coded HAL property ID to enum value set. For unit test only. 155 * 156 * TODO(b/293354967): Remove this once we migrate to runtime config. 157 */ 158 @VisibleForTesting getHalPropIdToEnumSet()159 /* package */ SparseArray<Set<Integer>> getHalPropIdToEnumSet() { 160 return mHalPropIdToEnumSet; 161 } 162 163 /** 164 * Gets a list of all system VHAL property IDs. For unit test only. 165 */ 166 @VisibleForTesting getAllSystemHalPropIds()167 /* package */ List<Integer> getAllSystemHalPropIds() { 168 List<Integer> halPropIds = new ArrayList<>(); 169 for (int i = 0; i < mHalPropIdToCarSvcConfig.size(); i++) { 170 halPropIds.add(mHalPropIdToCarSvcConfig.keyAt(i)); 171 } 172 return halPropIds; 173 } 174 175 /** 176 * Gets the singleton instance for {@link PropertyHalServiceConfigs}. 177 */ getInstance()178 public static PropertyHalServiceConfigs getInstance() { 179 synchronized (sLock) { 180 if (sPropertyHalServiceConfigs == null) { 181 sPropertyHalServiceConfigs = new PropertyHalServiceConfigs( 182 /* featureFlags= */ null); 183 } 184 return sPropertyHalServiceConfigs; 185 } 186 } 187 188 /** 189 * Create a new instance for {@link PropertyHalServiceConfigs}. 190 */ 191 @VisibleForTesting newConfigs()192 public static PropertyHalServiceConfigs newConfigs() { 193 return new PropertyHalServiceConfigs(/* featureFlags= */ null); 194 } 195 196 /** 197 * Returns all possible supported enum values for the {@code halPropId}. If property does not 198 * support an enum, then it returns {@code null}. 199 */ 200 @Nullable getAllPossibleSupportedEnumValues(int halPropId)201 public Set<Integer> getAllPossibleSupportedEnumValues(int halPropId) { 202 if (!mHalPropIdToCarSvcConfig.contains(halPropId)) { 203 return null; 204 } 205 Set<Integer> dataEnums = mHalPropIdToCarSvcConfig.get(halPropId).dataEnums; 206 return dataEnums != null ? Collections.unmodifiableSet(dataEnums) : null; 207 } 208 209 /** 210 * Checks property value's format for all properties. Checks property value range if property 211 * has @data_enum flag in types.hal. 212 * @return true if property value's payload is valid. 213 */ checkPayload(HalPropValue propValue)214 public boolean checkPayload(HalPropValue propValue) { 215 int propId = propValue.getPropId(); 216 // Mixed property uses config array to indicate the data format. Checked it when convert it 217 // to CarPropertyValue. 218 if ((propId & VehiclePropertyType.MASK) == VehiclePropertyType.MIXED) { 219 return true; 220 } 221 if (propValue.getStatus() != VehiclePropertyStatus.AVAILABLE) { 222 return true; 223 } 224 if (!checkFormatForAllProperties(propValue)) { 225 Slogf.e(TAG, "Property value: " + propValue + " has an invalid data format"); 226 return false; 227 } 228 CarSvcPropertyConfig carSvcPropertyConfig = mHalPropIdToCarSvcConfig.get(propId); 229 if (carSvcPropertyConfig == null) { 230 // This is not a system property. 231 return true; 232 } 233 if (carSvcPropertyConfig.dataEnums() != null) { 234 return checkDataEnum(propValue, carSvcPropertyConfig.dataEnums()); 235 } 236 if (carSvcPropertyConfig.validBitFlag() != null) { 237 return checkValidBitFlag(propValue, carSvcPropertyConfig.validBitFlag()); 238 } 239 return true; 240 } 241 242 /** 243 * Gets readPermission using a HAL-level propertyId. 244 * 245 * @param halPropId HAL-level propertyId 246 * @return PermissionCondition object that represents halPropId's readPermission 247 */ 248 @Nullable getReadPermission(int halPropId)249 public PermissionCondition getReadPermission(int halPropId) { 250 if (CarPropertyHelper.isVendorOrBackportedProperty(halPropId)) { 251 return getVendorReadPermission(halPropId); 252 } 253 CarSvcPropertyConfig carSvcPropertyConfig = mHalPropIdToCarSvcConfig.get( 254 halPropId); 255 if (carSvcPropertyConfig == null) { 256 Slogf.w(TAG, "halPropId: " + halPropIdToName(halPropId) + " is not supported," 257 + " no read permission"); 258 return null; 259 } 260 return carSvcPropertyConfig.permissions().readPermission(); 261 } 262 263 @Nullable getVendorReadPermission(int halPropId)264 private PermissionCondition getVendorReadPermission(int halPropId) { 265 String halPropIdName = halPropIdToName(halPropId); 266 synchronized (mLock) { 267 PropertyPermissions propertyPermissions = mVendorHalPropIdToPermissions.get(halPropId); 268 if (propertyPermissions == null) { 269 Slogf.v(TAG, "no custom vendor read permission for: " + halPropIdName 270 + ", default to PERMISSION_VENDOR_EXTENSION"); 271 return SINGLE_PERMISSION_VENDOR_EXTENSION; 272 } 273 PermissionCondition readPermission = propertyPermissions.readPermission(); 274 if (readPermission == null) { 275 Slogf.v(TAG, "vendor propId is not available for reading: " + halPropIdName); 276 } 277 return readPermission; 278 } 279 } 280 281 /** 282 * Gets writePermission using a HAL-level propertyId. 283 * 284 * @param halPropId HAL-level propertyId 285 * @return PermissionCondition object that represents halPropId's writePermission 286 */ 287 @Nullable getWritePermission(int halPropId)288 public PermissionCondition getWritePermission(int halPropId) { 289 if (CarPropertyHelper.isVendorOrBackportedProperty(halPropId)) { 290 return getVendorWritePermission(halPropId); 291 } 292 CarSvcPropertyConfig carSvcPropertyConfig = mHalPropIdToCarSvcConfig.get( 293 halPropId); 294 if (carSvcPropertyConfig == null) { 295 Slogf.w(TAG, "halPropId: " + halPropIdToName(halPropId) + " is not supported," 296 + " no write permission"); 297 return null; 298 } 299 return carSvcPropertyConfig.permissions().writePermission(); 300 } 301 302 @Nullable getVendorWritePermission(int halPropId)303 private PermissionCondition getVendorWritePermission(int halPropId) { 304 String halPropIdName = halPropIdToName(halPropId); 305 synchronized (mLock) { 306 PropertyPermissions propertyPermissions = mVendorHalPropIdToPermissions.get(halPropId); 307 if (propertyPermissions == null) { 308 Slogf.v(TAG, "no custom vendor write permission for: " + halPropIdName 309 + ", default to PERMISSION_VENDOR_EXTENSION"); 310 return SINGLE_PERMISSION_VENDOR_EXTENSION; 311 } 312 PermissionCondition writePermission = propertyPermissions.writePermission(); 313 if (writePermission == null) { 314 Slogf.v(TAG, "vendor propId is not available for writing: " + halPropIdName); 315 } 316 return writePermission; 317 } 318 } 319 320 /** 321 * Checks if readPermission is granted for a HAL-level propertyId in a given context. 322 * 323 * @param halPropId HAL-level propertyId 324 * @param context Context to check 325 * @return readPermission is granted or not. 326 */ isReadable(Context context, int halPropId)327 public boolean isReadable(Context context, int halPropId) { 328 PermissionCondition readPermission = getReadPermission(halPropId); 329 if (readPermission == null) { 330 Slogf.v(TAG, "propId is not readable: " + halPropIdToName(halPropId)); 331 return false; 332 } 333 return readPermission.isMet(context); 334 } 335 336 /** 337 * Checks if writePermission is granted for a HAL-level propertyId in a given context. 338 * 339 * @param halPropId HAL-level propertyId 340 * @param context Context to check 341 * @return writePermission is granted or not. 342 */ isWritable(Context context, int halPropId)343 public boolean isWritable(Context context, int halPropId) { 344 PermissionCondition writePermission = getWritePermission(halPropId); 345 if (writePermission == null) { 346 Slogf.v(TAG, "propId is not writable: " + halPropIdToName(halPropId)); 347 return false; 348 } 349 return writePermission.isMet(context); 350 } 351 352 /** 353 * Checks if property ID is in the list of known IDs that PropertyHalService is interested it. 354 */ isSupportedProperty(int propId)355 public boolean isSupportedProperty(int propId) { 356 return mHalPropIdToCarSvcConfig.get(propId) != null 357 || CarPropertyHelper.isVendorOrBackportedProperty(propId); 358 } 359 360 /** 361 * Overrides the permission map for vendor properties 362 * 363 * @param configArray the configArray for 364 * {@link VehicleProperty#SUPPORT_CUSTOMIZE_VENDOR_PERMISSION} 365 */ customizeVendorPermission(int[] configArray)366 public void customizeVendorPermission(int[] configArray) { 367 if (configArray == null || configArray.length % 3 != 0) { 368 throw new IllegalArgumentException( 369 "ConfigArray for SUPPORT_CUSTOMIZE_VENDOR_PERMISSION is wrong"); 370 } 371 int index = 0; 372 while (index < configArray.length) { 373 int propId = configArray[index++]; 374 if (!CarPropertyHelper.isVendorOrBackportedProperty(propId)) { 375 throw new IllegalArgumentException("Property Id: " + propId 376 + " is not in vendor range"); 377 } 378 int readPermission = configArray[index++]; 379 int writePermission = configArray[index++]; 380 String readPermissionStr = PropertyPermissionInfo.toPermissionString( 381 readPermission, propId); 382 String writePermissionStr = PropertyPermissionInfo.toPermissionString( 383 writePermission, propId); 384 synchronized (mLock) { 385 if (mVendorHalPropIdToPermissions.get(propId) != null) { 386 Slogf.e(TAG, "propId is a vendor property that already exists in " 387 + "mVendorHalPropIdToPermissions and thus cannot have its " 388 + "permissions overwritten: " + halPropIdToName(propId)); 389 continue; 390 } 391 392 PropertyPermissionsBuilder propertyPermissionBuilder = 393 new PropertyPermissionsBuilder(); 394 if (readPermissionStr != null) { 395 propertyPermissionBuilder.setReadPermission( 396 new SinglePermission(readPermissionStr)); 397 } 398 if (writePermissionStr != null) { 399 propertyPermissionBuilder.setWritePermission( 400 new SinglePermission(writePermissionStr)); 401 } 402 403 mVendorHalPropIdToPermissions.put(propId, propertyPermissionBuilder.build()); 404 } 405 } 406 } 407 408 /** 409 * Converts manager property ID to Vehicle HAL property ID. 410 */ managerToHalPropId(int mgrPropId)411 public int managerToHalPropId(int mgrPropId) { 412 return mMgrPropIdToHalPropId.getValue(mgrPropId, mgrPropId); 413 } 414 415 /** 416 * Converts Vehicle HAL property ID to manager property ID. 417 */ halToManagerPropId(int halPropId)418 public int halToManagerPropId(int halPropId) { 419 return mMgrPropIdToHalPropId.getKey(halPropId, halPropId); 420 } 421 422 /** 423 * Print out the name for a VHAL property Id. 424 * 425 * For debugging only. 426 */ halPropIdToName(int halPropId)427 public String halPropIdToName(int halPropId) { 428 return VehiclePropertyIds.toString(halToManagerPropId(halPropId)); 429 } 430 checkValidBitFlag(HalPropValue propValue, int flagCombination)431 private static boolean checkValidBitFlag(HalPropValue propValue, int flagCombination) { 432 for (int i = 0; i < propValue.getInt32ValuesSize(); i++) { 433 int value = propValue.getInt32Value(i); 434 if ((value & flagCombination) != value) { 435 return false; 436 } 437 } 438 return true; 439 } 440 441 /** 442 * Parses a car service JSON config file. Only exposed for testing. 443 */ 444 @VisibleForTesting parseJsonConfig(InputStream configFile, String path)445 /* package */ SparseArray<CarSvcPropertyConfig> parseJsonConfig(InputStream configFile, 446 String path) { 447 try { 448 SparseArray<CarSvcPropertyConfig> configs = new SparseArray<>(); 449 try (var reader = new JsonReader(new InputStreamReader(configFile, "UTF-8"))) { 450 reader.setLenient(true); 451 parseObjectEntry(reader, () -> { 452 if (!reader.nextName().equals(JSON_FIELD_NAME_PROPERTIES)) { 453 reader.skipValue(); 454 return; 455 } 456 parseObjectEntry(reader, () -> { 457 String propertyName = reader.nextName(); 458 CarSvcPropertyConfig config; 459 try { 460 config = readPropertyObject(propertyName, reader); 461 } catch (IllegalArgumentException e) { 462 throw new IllegalArgumentException("Invalid json config for property: " 463 + propertyName + ", error: " + e); 464 } 465 if (config == null) { 466 return; 467 } 468 configs.put(config.halPropId(), config); 469 }); 470 }); 471 } 472 return configs; 473 } catch (IllegalStateException | IOException e) { 474 throw new IllegalArgumentException("Config file: " + path 475 + " does not contain a valid JSON object.", e); 476 } 477 } 478 readPropertyObject( String propertyName, JsonReader reader)479 private @Nullable CarSvcPropertyConfig readPropertyObject( 480 String propertyName, JsonReader reader) throws IOException { 481 String featureFlag = null; 482 boolean deprecated = false; 483 int propertyId = 0; 484 int vhalPropertyId = 0; 485 String description = null; 486 ArraySet<Integer> dataEnums = new ArraySet<Integer>(); 487 List<Integer> dataFlags = new ArrayList<>(); 488 PermissionCondition readPermission = null; 489 PermissionCondition writePermission = null; 490 // Starts parsing each field. 491 reader.beginObject(); 492 while (reader.hasNext()) { 493 String name = reader.nextName(); 494 switch (name) { 495 case "featureFlag": 496 featureFlag = reader.nextString(); 497 break; 498 case "deprecated": 499 deprecated = reader.nextBoolean(); 500 break; 501 case "propertyId": 502 propertyId = reader.nextInt(); 503 break; 504 case "vhalPropertyId": 505 vhalPropertyId = reader.nextInt(); 506 break; 507 case "description": 508 description = reader.nextString(); 509 break; 510 case "dataEnums": 511 reader.beginArray(); 512 while (reader.hasNext()) { 513 dataEnums.add(reader.nextInt()); 514 } 515 reader.endArray(); 516 break; 517 case "dataFlags": 518 reader.beginArray(); 519 while (reader.hasNext()) { 520 dataFlags.add(reader.nextInt()); 521 } 522 reader.endArray(); 523 break; 524 case "readPermission": 525 try { 526 readPermission = parsePermissionCondition(reader); 527 } catch (IllegalArgumentException e) { 528 throw new IllegalArgumentException( 529 "Failed to parse read permissions for property: " + propertyName 530 + ", error: " + e); 531 } 532 break; 533 case "writePermission": 534 try { 535 writePermission = parsePermissionCondition(reader); 536 } catch (IllegalArgumentException e) { 537 throw new IllegalArgumentException( 538 "Failed to parse write permissions for property: " + propertyName 539 + ", error: " + e); 540 } 541 break; 542 default: 543 reader.skipValue(); 544 } 545 } 546 reader.endObject(); 547 548 // Finished parsing each field, now check whether the required fields are present and 549 // assign them to config. 550 if (deprecated) { 551 return null; 552 } 553 if (featureFlag != null) { 554 switch (featureFlag) { 555 case VIC_FLAG_NAME: 556 // do nothing since Android V release is already cut 557 break; 558 case REMOVE_SYSTEM_API_TAGS_FLAG_NAME: 559 // do nothing as no behavior change 560 break; 561 case FLAG_25Q2_3P_PERMISSIONS: 562 // do nothing as no behavior change 563 break; 564 case B_FLAG_NAME: 565 if (!mFeatureFlags.androidBVehicleProperties()) { 566 Slogf.w(TAG, "The required feature flag for property: %s is not enabled, " 567 + "so its config is ignored", propertyName); 568 return null; 569 } 570 break; 571 default: 572 throw new IllegalArgumentException("Unknown feature flag: " 573 + featureFlag + " for property: " + propertyName); 574 } 575 } 576 if (description == null) { 577 throw new IllegalArgumentException("Missing required description field for property: " 578 + propertyName); 579 } 580 if (propertyId == 0) { 581 throw new IllegalArgumentException("Missing required propertyId field for property: " 582 + propertyName); 583 } 584 int halPropId; 585 if (vhalPropertyId != 0) { 586 halPropId = vhalPropertyId; 587 } else { 588 halPropId = propertyId; 589 } 590 if (dataEnums.isEmpty()) { 591 dataEnums = null; 592 } 593 Integer validBitFlag = null; 594 if (!dataFlags.isEmpty()) { 595 validBitFlag = generateAllCombination(dataFlags); 596 } 597 if (readPermission == null && writePermission == null) { 598 throw new IllegalArgumentException( 599 "No read or write permission specified for: " + propertyName); 600 } 601 var builder = new PropertyPermissionsBuilder(); 602 if (readPermission != null) { 603 builder.setReadPermission(readPermission); 604 } 605 if (writePermission != null) { 606 builder.setWritePermission(writePermission); 607 } 608 PropertyPermissions permissions = builder.build(); 609 return new CarSvcPropertyConfig(propertyId, halPropId, propertyName, description, 610 permissions, dataEnums, validBitFlag); 611 } 612 613 private interface RunanbleWithException { run()614 void run() throws IOException; 615 } 616 parseObjectEntry(JsonReader reader, RunanbleWithException forEachEntry)617 private static void parseObjectEntry(JsonReader reader, RunanbleWithException forEachEntry) 618 throws IOException { 619 reader.beginObject(); 620 while (reader.hasNext()) { 621 forEachEntry.run(); 622 } 623 reader.endObject(); 624 } 625 parsePermissionCondition(JsonReader reader)626 private static PermissionCondition parsePermissionCondition(JsonReader reader) 627 throws IOException { 628 // we only have one type, use a list to be effective-final. 629 List<String> types = new ArrayList<>(); 630 List<PermissionCondition> permissions = new ArrayList<>(); 631 parseObjectEntry(reader, () -> { 632 String name = reader.nextName(); 633 switch (name) { 634 case "type": 635 types.add(reader.nextString()); 636 break; 637 case "value": 638 try { 639 permissions.add(new SinglePermission(reader.nextString())); 640 } catch (IllegalStateException e) { 641 // The value field is not a string, then it must be an array. 642 reader.beginArray(); 643 while (reader.hasNext()) { 644 permissions.add(parsePermissionCondition(reader)); 645 } 646 reader.endArray(); 647 } 648 break; 649 default: 650 reader.skipValue(); 651 } 652 }); 653 if (types.size() == 0) { 654 throw new IllegalArgumentException("Missing type field for permission"); 655 } 656 String type = types.get(0); 657 if (permissions.size() < 1) { 658 throw new IllegalArgumentException("Missing valid value field for permission"); 659 } 660 if (type.equals("single")) { 661 return permissions.get(0); 662 } 663 if (type.equals("anyOf")) { 664 return new AnyOfPermissions(permissions.toArray(new PermissionCondition[0])); 665 } 666 if (type.equals("allOf")) { 667 return new AllOfPermissions(permissions.toArray(new PermissionCondition[0])); 668 } 669 throw new IllegalArgumentException("Invalid permission type: " + type); 670 } 671 checkFormatForAllProperties(HalPropValue propValue)672 private static boolean checkFormatForAllProperties(HalPropValue propValue) { 673 int propId = propValue.getPropId(); 674 int vehiclePropertyType = propId & VehiclePropertyType.MASK; 675 676 // Records sum size of int32values, floatValue, int64Values, bytes, String 677 int sizeOfAllValue = propValue.getInt32ValuesSize() + propValue.getFloatValuesSize() 678 + propValue.getInt64ValuesSize() + propValue.getByteValuesSize() 679 + propValue.getStringValue().length(); 680 if (sizeOfAllValue == 0 681 && vehiclePropertyType != VehiclePropertyType.FLOAT_VEC 682 && vehiclePropertyType != VehiclePropertyType.INT64_VEC 683 && vehiclePropertyType != VehiclePropertyType.INT32_VEC) { 684 Slogf.e(TAG, "Property value is empty: " + propValue); 685 return false; 686 } 687 688 switch (vehiclePropertyType) { 689 case VehiclePropertyType.BOOLEAN: 690 case VehiclePropertyType.INT32: 691 return sizeOfAllValue == 1 && propValue.getInt32ValuesSize() == 1; 692 case VehiclePropertyType.FLOAT: 693 return sizeOfAllValue == 1 && propValue.getFloatValuesSize() == 1; 694 case VehiclePropertyType.INT64: 695 return sizeOfAllValue == 1 && propValue.getInt64ValuesSize() == 1; 696 case VehiclePropertyType.FLOAT_VEC: 697 return sizeOfAllValue == propValue.getFloatValuesSize(); 698 case VehiclePropertyType.INT64_VEC: 699 return sizeOfAllValue == propValue.getInt64ValuesSize(); 700 case VehiclePropertyType.INT32_VEC: 701 return sizeOfAllValue == propValue.getInt32ValuesSize(); 702 case VehiclePropertyType.BYTES: 703 return sizeOfAllValue == propValue.getByteValuesSize(); 704 case VehiclePropertyType.STRING: 705 return sizeOfAllValue == propValue.getStringValue().length(); 706 default: 707 throw new IllegalArgumentException("Unexpected property type for propId: " 708 + Integer.toHexString(propId)); 709 } 710 } 711 checkDataEnum(HalPropValue propValue, Set<Integer> enums)712 private static boolean checkDataEnum(HalPropValue propValue, Set<Integer> enums) { 713 for (int i = 0; i < propValue.getInt32ValuesSize(); i++) { 714 if (!enums.contains(propValue.getInt32Value(i))) { 715 return false; 716 } 717 } 718 return true; 719 } 720 generateAllCombination(List<Integer> bitFlags)721 private static int generateAllCombination(List<Integer> bitFlags) { 722 int combination = bitFlags.get(0); 723 for (int i = 1; i < bitFlags.size(); i++) { 724 combination |= bitFlags.get(i); 725 } 726 return combination; 727 } 728 } 729