1 /* 2 * Copyright (C) 2020 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.power; 18 19 import static android.car.hardware.power.PowerComponentUtil.FIRST_POWER_COMPONENT; 20 import static android.car.hardware.power.PowerComponentUtil.INVALID_POWER_COMPONENT; 21 import static android.car.hardware.power.PowerComponentUtil.LAST_POWER_COMPONENT; 22 import static android.car.hardware.power.PowerComponentUtil.powerComponentToString; 23 import static android.car.hardware.power.PowerComponentUtil.toPowerComponent; 24 import static android.frameworks.automotive.powerpolicy.PowerComponent.MINIMUM_CUSTOM_COMPONENT_VALUE; 25 26 import static com.android.car.internal.common.CommonConstants.EMPTY_INT_ARRAY; 27 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 28 29 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 30 import static org.xmlpull.v1.XmlPullParser.END_TAG; 31 import static org.xmlpull.v1.XmlPullParser.START_TAG; 32 import static org.xmlpull.v1.XmlPullParser.TEXT; 33 34 import android.annotation.Nullable; 35 import android.car.builtin.util.Slogf; 36 import android.car.feature.FeatureFlags; 37 import android.car.hardware.power.CarPowerPolicy; 38 import android.car.hardware.power.PowerComponent; 39 import android.hardware.automotive.vehicle.VehicleApPowerStateReport; 40 import android.util.ArrayMap; 41 import android.util.ArraySet; 42 import android.util.SparseArray; 43 import android.util.SparseBooleanArray; 44 import android.util.Xml; 45 import android.util.proto.ProtoOutputStream; 46 47 import com.android.car.CarLog; 48 import com.android.car.CarServiceUtils; 49 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 50 import com.android.car.internal.util.IndentingPrintWriter; 51 import com.android.car.internal.util.Lists; 52 import com.android.car.power.CarPowerDumpProto.PolicyReaderProto; 53 import com.android.car.power.CarPowerDumpProto.PolicyReaderProto.ComponentNameToValue; 54 import com.android.car.power.CarPowerDumpProto.PolicyReaderProto.IdToPolicyGroup; 55 import com.android.car.power.CarPowerDumpProto.PolicyReaderProto.IdToPolicyGroup.PolicyGroup; 56 import com.android.car.power.CarPowerDumpProto.PolicyReaderProto.IdToPolicyGroup.PolicyGroup.StateToDefaultPolicy; 57 import com.android.car.power.CarPowerDumpProto.PolicyReaderProto.PowerPolicy; 58 import com.android.internal.annotations.VisibleForTesting; 59 60 import org.xmlpull.v1.XmlPullParser; 61 import org.xmlpull.v1.XmlPullParserException; 62 63 import java.io.FileInputStream; 64 import java.io.IOException; 65 import java.io.InputStream; 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Set; 71 72 /** 73 * Helper class to read and manage vendor power policies. 74 * 75 * <p>{@code CarPowerManagementService} manages power policies through {@code PolicyReader}. This 76 * class is not thread-safe, and must be used in the main thread or with additional serialization. 77 */ 78 public final class PolicyReader { 79 public static final String POWER_STATE_WAIT_FOR_VHAL = "WaitForVHAL"; 80 public static final String POWER_STATE_ON = "On"; 81 82 // TODO(b/286303350): Remove all system power policy definitions after refactor is complete 83 static final String SYSTEM_POWER_POLICY_PREFIX = "system_power_policy_"; 84 // Preemptive system power policy used for disabling user interaction in Silent Mode or Garage 85 // Mode. 86 static final String POWER_POLICY_ID_NO_USER_INTERACTION = SYSTEM_POWER_POLICY_PREFIX 87 + "no_user_interaction"; 88 // Preemptive system power policy used for preparing Suspend-to-RAM. 89 static final String POWER_POLICY_ID_SUSPEND_PREP = SYSTEM_POWER_POLICY_PREFIX 90 + "suspend_prep"; 91 // Non-preemptive system power policy used for turning all components on. 92 static final String POWER_POLICY_ID_ALL_ON = SYSTEM_POWER_POLICY_PREFIX + "all_on"; 93 // Non-preemptive system power policy used to represent minimal on state. 94 static final String POWER_POLICY_ID_INITIAL_ON = SYSTEM_POWER_POLICY_PREFIX + "initial_on"; 95 96 static final int INVALID_POWER_STATE = -1; 97 98 private static final String TAG = CarLog.tagFor(PolicyReader.class); 99 private static final String VENDOR_POLICY_PATH = "/vendor/etc/automotive/power_policy.xml"; 100 101 private static final String NAMESPACE = null; 102 private static final Set<String> VALID_VERSIONS = new ArraySet<>(Arrays.asList("1.0")); 103 private static final String TAG_POWER_POLICY = "powerPolicy"; 104 private static final String TAG_POLICY_GROUPS = "policyGroups"; 105 private static final String TAG_POLICY_GROUP = "policyGroup"; 106 private static final String TAG_DEFAULT_POLICY = "defaultPolicy"; 107 private static final String TAG_NO_DEFAULT_POLICY = "noDefaultPolicy"; 108 private static final String TAG_POLICIES = "policies"; 109 private static final String TAG_POLICY = "policy"; 110 private static final String TAG_OTHER_COMPONENTS = "otherComponents"; 111 private static final String TAG_COMPONENT = "component"; 112 private static final String TAG_SYSTEM_POLICY_OVERRIDES = "systemPolicyOverrides"; 113 private static final String TAG_CUSTOM_COMPONENTS = "customComponents"; 114 private static final String TAG_CUSTOM_COMPONENT = "customComponent"; 115 private static final String ATTR_DEFAULT_POLICY_GROUP = "defaultPolicyGroup"; 116 private static final String ATTR_VERSION = "version"; 117 private static final String ATTR_ID = "id"; 118 private static final String ATTR_STATE = "state"; 119 private static final String ATTR_BEHAVIOR = "behavior"; 120 private static final String POWER_ONOFF_ON = "on"; 121 private static final String POWER_ONOFF_OFF = "off"; 122 private static final String POWER_ONOFF_UNTOUCHED = "untouched"; 123 124 private static final int[] ALL_COMPONENTS; 125 private static final int[] NO_COMPONENTS = EMPTY_INT_ARRAY; 126 private static final int[] INITIAL_ON_COMPONENTS = { 127 PowerComponent.AUDIO, PowerComponent.DISPLAY, PowerComponent.CPU 128 }; 129 private static final int[] NO_USER_INTERACTION_ENABLED_COMPONENTS = { 130 PowerComponent.WIFI, PowerComponent.CELLULAR, 131 PowerComponent.ETHERNET, PowerComponent.TRUSTED_DEVICE_DETECTION, PowerComponent.CPU 132 }; 133 private static final int[] NO_USER_INTERACTION_DISABLED_COMPONENTS = { 134 PowerComponent.AUDIO, PowerComponent.MEDIA, PowerComponent.DISPLAY, 135 PowerComponent.BLUETOOTH, PowerComponent.PROJECTION, PowerComponent.NFC, 136 PowerComponent.INPUT, PowerComponent.VOICE_INTERACTION, 137 PowerComponent.VISUAL_INTERACTION, PowerComponent.LOCATION, PowerComponent.MICROPHONE 138 }; 139 private static final Set<Integer> SYSTEM_POLICY_CONFIGURABLE_COMPONENTS = 140 new ArraySet<>(Arrays.asList(PowerComponent.BLUETOOTH, PowerComponent.NFC, 141 PowerComponent.TRUSTED_DEVICE_DETECTION)); 142 private static final int[] SUSPEND_PREP_DISABLED_COMPONENTS = { 143 PowerComponent.AUDIO, PowerComponent.BLUETOOTH, PowerComponent.WIFI, 144 PowerComponent.LOCATION, PowerComponent.MICROPHONE, PowerComponent.CPU 145 }; 146 private static final CarPowerPolicy POWER_POLICY_ALL_ON; 147 private static final CarPowerPolicy POWER_POLICY_INITIAL_ON; 148 private static final CarPowerPolicy POWER_POLICY_SUSPEND_PREP; 149 150 static { 151 int allCount = LAST_POWER_COMPONENT - FIRST_POWER_COMPONENT + 1; 152 ALL_COMPONENTS = new int[allCount]; 153 int[] initialOnDisabledComponents = new int[allCount - INITIAL_ON_COMPONENTS.length]; 154 int pos = 0; 155 for (int c = FIRST_POWER_COMPONENT; c <= LAST_POWER_COMPONENT; c++) { 156 ALL_COMPONENTS[c - FIRST_POWER_COMPONENT] = c; 157 if (!containsComponent(INITIAL_ON_COMPONENTS, c)) { 158 initialOnDisabledComponents[pos++] = c; 159 } 160 } 161 162 POWER_POLICY_ALL_ON = new CarPowerPolicy(POWER_POLICY_ID_ALL_ON, ALL_COMPONENTS.clone(), 163 NO_COMPONENTS.clone()); 164 POWER_POLICY_INITIAL_ON = new CarPowerPolicy(POWER_POLICY_ID_INITIAL_ON, 165 INITIAL_ON_COMPONENTS.clone(), initialOnDisabledComponents); 166 POWER_POLICY_SUSPEND_PREP = new CarPowerPolicy(POWER_POLICY_ID_SUSPEND_PREP, 167 NO_COMPONENTS.clone(), SUSPEND_PREP_DISABLED_COMPONENTS.clone()); 168 } 169 // Allows for injecting mock feature flag values during testing 170 private FeatureFlags mFeatureFlags; 171 172 private ArrayMap<String, CarPowerPolicy> mRegisteredPowerPolicies; 173 private ArrayMap<String, SparseArray<String>> mPolicyGroups; 174 // TODO(b/286303350): remove once power policy refactor complete 175 private ArrayMap<String, CarPowerPolicy> mPreemptivePowerPolicies; 176 // TODO(b/286303350): remove once power policy refactor complete 177 private String mDefaultPolicyGroupId; 178 private ArrayMap<String, Integer> mCustomComponents = new ArrayMap<>(); 179 180 /** 181 * Gets {@code CarPowerPolicy} corresponding to the given policy ID. 182 */ 183 @Nullable getPowerPolicy(String policyId)184 CarPowerPolicy getPowerPolicy(String policyId) { 185 return mRegisteredPowerPolicies.get(policyId); 186 } 187 188 /** 189 * Gets {@code CarPowerPolicy} corresponding to the given power state in the given power 190 * policy group. 191 */ 192 @Nullable getDefaultPowerPolicyForState(String groupId, int state)193 CarPowerPolicy getDefaultPowerPolicyForState(String groupId, int state) { 194 SparseArray<String> group = mPolicyGroups.get( 195 (groupId == null || groupId.isEmpty()) ? mDefaultPolicyGroupId : groupId); 196 if (group == null) { 197 return null; 198 } 199 String policyId = group.get(state); 200 if (policyId == null) { 201 return null; 202 } 203 return mRegisteredPowerPolicies.get(policyId); 204 } 205 206 /** 207 * Gets the preemptive power policy corresponding to the given policy ID. 208 * 209 * <p> When a preemptive power policy is the current power policy, applying a regular power 210 * policy is deferred until the preemptive power policy is released. 211 */ 212 @Nullable getPreemptivePowerPolicy(String policyId)213 CarPowerPolicy getPreemptivePowerPolicy(String policyId) { 214 return mPreemptivePowerPolicies.get(policyId); 215 } 216 isPowerPolicyGroupAvailable(String groupId)217 boolean isPowerPolicyGroupAvailable(String groupId) { 218 return mPolicyGroups.containsKey(groupId); 219 } 220 isPreemptivePowerPolicy(String policyId)221 boolean isPreemptivePowerPolicy(String policyId) { 222 return mPreemptivePowerPolicies.containsKey(policyId); 223 } 224 225 /** 226 * Gets default power policy group ID. 227 * 228 * @return {@code String} containing power policy group ID or {@code null} if it is not defined 229 */ 230 @Nullable getDefaultPowerPolicyGroup()231 String getDefaultPowerPolicyGroup() { 232 return mDefaultPolicyGroupId; 233 } 234 init(FeatureFlags fakeFeatureFlags)235 void init(FeatureFlags fakeFeatureFlags) { 236 mFeatureFlags = fakeFeatureFlags; 237 Slogf.d(TAG, "PolicyReader is initializing, carPowerPolicyRefactoring = " 238 + mFeatureFlags.carPowerPolicyRefactoring()); 239 initPolicies(); 240 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 241 readPowerPolicyConfiguration(); 242 } 243 } 244 245 /** 246 * Creates and registers a new power policy. 247 * 248 * @return {@code PolicyOperationStatus.OK}, if successful. Otherwise, the other values. 249 */ 250 @PolicyOperationStatus.ErrorCode definePowerPolicy(String policyId, String[] enabledComponents, String[] disabledComponents)251 int definePowerPolicy(String policyId, String[] enabledComponents, 252 String[] disabledComponents) { 253 // policyId cannot be empty or null 254 if (policyId == null || policyId.length() == 0) { 255 int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID; 256 Slogf.w(TAG, 257 PolicyOperationStatus.errorCodeToString(error, "policyId cannot be empty")); 258 return error; 259 } 260 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 261 if (isSystemPowerPolicy(policyId)) { 262 int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID; 263 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, 264 "policyId should not start with " + SYSTEM_POWER_POLICY_PREFIX)); 265 return error; 266 } 267 } 268 if (mRegisteredPowerPolicies.containsKey(policyId)) { 269 int error = PolicyOperationStatus.ERROR_DOUBLE_REGISTERED_POWER_POLICY_ID; 270 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId)); 271 return error; 272 } 273 SparseBooleanArray components = new SparseBooleanArray(); 274 int status = parseComponents(enabledComponents, true, components); 275 if (status != PolicyOperationStatus.OK) { 276 return status; 277 } 278 status = parseComponents(disabledComponents, false, components); 279 if (status != PolicyOperationStatus.OK) { 280 return status; 281 } 282 CarPowerPolicy policy = new CarPowerPolicy(policyId, toIntArray(components, true), 283 toIntArray(components, false)); 284 mRegisteredPowerPolicies.put(policyId, policy); 285 return PolicyOperationStatus.OK; 286 } 287 288 /** 289 * Defines and registers a new power policy group. 290 * 291 * @return {@code PolicyOperationStatus.OK}, if successful. Otherwise, the other values. 292 */ 293 @PolicyOperationStatus.ErrorCode definePowerPolicyGroup(String policyGroupId, SparseArray<String> defaultPolicyPerState)294 int definePowerPolicyGroup(String policyGroupId, SparseArray<String> defaultPolicyPerState) { 295 if (policyGroupId == null) { 296 return PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_GROUP_ID; 297 } 298 if (mPolicyGroups.containsKey(policyGroupId)) { 299 int error = PolicyOperationStatus.ERROR_DOUBLE_REGISTERED_POWER_POLICY_GROUP_ID; 300 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyGroupId)); 301 return error; 302 } 303 for (int i = 0; i < defaultPolicyPerState.size(); i++) { 304 int state = defaultPolicyPerState.keyAt(i); 305 String policyId = defaultPolicyPerState.valueAt(i); 306 if (!mRegisteredPowerPolicies.containsKey(policyId)) { 307 int error = PolicyOperationStatus.ERROR_NOT_REGISTERED_POWER_POLICY_ID; 308 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId + " for " 309 + vhalPowerStateToString(state))); 310 return error; 311 } 312 } 313 mPolicyGroups.put(policyGroupId, defaultPolicyPerState); 314 return PolicyOperationStatus.OK; 315 } 316 317 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)318 void dump(IndentingPrintWriter writer) { 319 int size = mCustomComponents.size(); 320 writer.printf("Registered custom components:"); 321 if (size == 0) { 322 writer.printf(" none\n"); 323 } else { 324 writer.printf("\n"); 325 writer.increaseIndent(); 326 for (int i = 0; i < size; i++) { 327 Object key = mCustomComponents.keyAt(i); 328 writer.printf("Component name: %s, value: %s\n", key, mCustomComponents.get(key)); 329 } 330 writer.decreaseIndent(); 331 } 332 333 size = mRegisteredPowerPolicies.size(); 334 writer.printf("Registered power policies:"); 335 if (size == 0) { 336 writer.printf(" none\n"); 337 } else { 338 writer.printf("\n"); 339 writer.increaseIndent(); 340 for (int i = 0; i < size; i++) { 341 writer.println(mRegisteredPowerPolicies.valueAt(i).toString()); 342 } 343 writer.decreaseIndent(); 344 } 345 346 size = mPolicyGroups.size(); 347 writer.printf("Power policy groups:"); 348 if (size == 0) { 349 writer.printf(" none\n"); 350 } else { 351 writer.printf("\n"); 352 writer.increaseIndent(); 353 for (int i = 0; i < size; i++) { 354 String key = mPolicyGroups.keyAt(i); 355 writer.println(key); 356 writer.increaseIndent(); 357 SparseArray<String> group = mPolicyGroups.get(key); 358 for (int j = 0; j < group.size(); j++) { 359 writer.printf("- %s --> %s\n", vhalPowerStateToString(group.keyAt(j)), 360 group.valueAt(j)); 361 } 362 writer.decreaseIndent(); 363 } 364 writer.decreaseIndent(); 365 } 366 writer.println("Preemptive power policy:"); 367 writer.increaseIndent(); 368 369 if (mFeatureFlags.carPowerPolicyRefactoring()) { 370 writer.println("Preemptive power policies not supported w/refactored power policy"); 371 } else { 372 for (int i = 0; i < mPreemptivePowerPolicies.size(); i++) { 373 writer.println(mPreemptivePowerPolicies.valueAt(i).toString()); 374 } 375 } 376 writer.decreaseIndent(); 377 } 378 379 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProtoPowerPolicies( ProtoOutputStream proto, long fieldNumber, ArrayMap<String, CarPowerPolicy> policies)380 void dumpProtoPowerPolicies( 381 ProtoOutputStream proto, long fieldNumber, ArrayMap<String, CarPowerPolicy> policies) { 382 for (int i = 0; i < policies.size(); i++) { 383 long policiesToken = proto.start(fieldNumber); 384 CarPowerPolicy powerPolicy = policies.valueAt(i); 385 proto.write(PowerPolicy.POLICY_ID, powerPolicy.getPolicyId()); 386 int[] enabledComponents = powerPolicy.getEnabledComponents(); 387 for (int j = 0; j < enabledComponents.length; j++) { 388 proto.write(PowerPolicy.ENABLED_COMPONENTS, 389 powerComponentToString(enabledComponents[j])); 390 } 391 int[] disabledComponents = powerPolicy.getDisabledComponents(); 392 for (int j = 0; j < disabledComponents.length; j++) { 393 proto.write(PowerPolicy.DISABLED_COMPONENTS, 394 powerComponentToString(disabledComponents[j])); 395 } 396 proto.end(policiesToken); 397 } 398 } 399 400 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)401 void dumpProto(ProtoOutputStream proto) { 402 long policyReaderToken = proto.start(CarPowerDumpProto.POLICY_READER); 403 404 for (int i = 0; i < mCustomComponents.size(); i++) { 405 long customComponentMappingsToken = proto.start( 406 PolicyReaderProto.CUSTOM_COMPONENT_MAPPINGS); 407 Object key = mCustomComponents.keyAt(i); 408 proto.write(ComponentNameToValue.COMPONENT_NAME, key.toString()); 409 proto.write(ComponentNameToValue.COMPONENT_VALUE, 410 mCustomComponents.get(key).intValue()); 411 proto.end(customComponentMappingsToken); 412 } 413 414 dumpProtoPowerPolicies( 415 proto, PolicyReaderProto.REGISTERED_POWER_POLICIES, mRegisteredPowerPolicies); 416 417 for (int i = 0; i < mPolicyGroups.size(); i++) { 418 long powerPolicyGroupMappingsToken = proto.start( 419 PolicyReaderProto.POWER_POLICY_GROUP_MAPPINGS); 420 String policyGroupId = mPolicyGroups.keyAt(i); 421 proto.write(IdToPolicyGroup.POLICY_GROUP_ID, policyGroupId); 422 SparseArray<String> group = mPolicyGroups.get(policyGroupId); 423 long policyGroupMappingsToken = proto.start(IdToPolicyGroup.POLICY_GROUP); 424 for (int j = 0; j < group.size(); j++) { 425 long defaultPolicyMappingsToken = proto.start(PolicyGroup.DEFAULT_POLICY_MAPPINGS); 426 proto.write(StateToDefaultPolicy.STATE, vhalPowerStateToString(group.keyAt(j))); 427 proto.write(StateToDefaultPolicy.DEFAULT_POLICY_ID, group.valueAt(j)); 428 proto.end(defaultPolicyMappingsToken); 429 } 430 proto.end(policyGroupMappingsToken); 431 proto.end(powerPolicyGroupMappingsToken); 432 } 433 434 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 435 dumpProtoPowerPolicies( 436 proto, PolicyReaderProto.PREEMPTIVE_POWER_POLICIES, mPreemptivePowerPolicies); 437 } 438 439 proto.end(policyReaderToken); 440 } 441 442 @VisibleForTesting initPolicies()443 void initPolicies() { 444 mRegisteredPowerPolicies = new ArrayMap<>(); 445 mPolicyGroups = new ArrayMap<>(); 446 447 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 448 registerBasicPowerPolicies(); 449 mPreemptivePowerPolicies = new ArrayMap<>(); 450 mPreemptivePowerPolicies.put(POWER_POLICY_ID_NO_USER_INTERACTION, 451 new CarPowerPolicy(POWER_POLICY_ID_NO_USER_INTERACTION, 452 NO_USER_INTERACTION_ENABLED_COMPONENTS.clone(), 453 NO_USER_INTERACTION_DISABLED_COMPONENTS.clone())); 454 mPreemptivePowerPolicies.put(POWER_POLICY_ID_SUSPEND_PREP, POWER_POLICY_SUSPEND_PREP); 455 } 456 } 457 readPowerPolicyConfiguration()458 private void readPowerPolicyConfiguration() { 459 try (InputStream inputStream = new FileInputStream(VENDOR_POLICY_PATH)) { 460 readPowerPolicyFromXml(inputStream); 461 } catch (IOException | XmlPullParserException | PolicyXmlException e) { 462 Slogf.w(TAG, "Proceed without registered policies: failed to parse %s: %s", 463 VENDOR_POLICY_PATH, e); 464 } 465 } 466 467 @VisibleForTesting readPowerPolicyFromXml(InputStream stream)468 void readPowerPolicyFromXml(InputStream stream) throws PolicyXmlException, 469 XmlPullParserException, IOException { 470 XmlPullParser parser = Xml.newPullParser(); 471 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, NAMESPACE != null); 472 parser.setInput(stream, null); 473 474 // Ensure <powerPolicy> is the root 475 parser.nextTag(); 476 parser.require(START_TAG, NAMESPACE, TAG_POWER_POLICY); 477 // Check version 478 String version = parser.getAttributeValue(NAMESPACE, ATTR_VERSION); 479 if (!VALID_VERSIONS.contains(version)) { 480 throw new PolicyXmlException("invalid XML version: " + version); 481 } 482 483 ArrayMap<String, CarPowerPolicy> registeredPolicies; 484 List<IntermediateCarPowerPolicy> intermediateCarPowerPolicies = new ArrayList<>(); 485 ArrayMap<String, SparseArray<String>> policyGroups = new ArrayMap<>(); 486 List<IntermediateCarPowerPolicy> intermediateSystemPolicyOverride = new ArrayList<>(); 487 ArrayMap<String, Integer> customComponents = new ArrayMap<>(); 488 CarPowerPolicy systemPolicyOverride; 489 String defaultGroupPolicyId = null; 490 491 int type; 492 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 493 if (type != START_TAG) continue; 494 switch (parser.getName()) { 495 case TAG_POLICIES: 496 intermediateCarPowerPolicies.addAll(parsePolicies(parser, true)); 497 break; 498 case TAG_POLICY_GROUPS: 499 defaultGroupPolicyId = parser.getAttributeValue(NAMESPACE, 500 ATTR_DEFAULT_POLICY_GROUP); 501 policyGroups = parsePolicyGroups(parser); 502 break; 503 case TAG_SYSTEM_POLICY_OVERRIDES: 504 intermediateSystemPolicyOverride.addAll(parseSystemPolicyOverrides(parser)); 505 break; 506 case TAG_CUSTOM_COMPONENTS: 507 customComponents = parseCustomComponents(parser); 508 break; 509 default: 510 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 511 + TAG_POWER_POLICY); 512 } 513 } 514 registeredPolicies = validatePowerPolicies(intermediateCarPowerPolicies, customComponents); 515 systemPolicyOverride = validateSystemOverrides(intermediateSystemPolicyOverride, 516 customComponents); 517 validatePolicyGroups(policyGroups, registeredPolicies, defaultGroupPolicyId); 518 519 mCustomComponents = customComponents; 520 mDefaultPolicyGroupId = defaultGroupPolicyId; 521 mRegisteredPowerPolicies = registeredPolicies; 522 registerBasicPowerPolicies(); 523 mPolicyGroups = policyGroups; 524 reconstructSystemPowerPolicy(systemPolicyOverride); 525 } 526 parseCustomComponents(XmlPullParser parser)527 private ArrayMap<String, Integer> parseCustomComponents(XmlPullParser parser) 528 throws XmlPullParserException, IOException, PolicyXmlException { 529 ArrayMap<String, Integer> customComponentsMap = new ArrayMap<>(); 530 int type; 531 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 532 if (type != START_TAG) continue; 533 if (TAG_CUSTOM_COMPONENT.equals(parser.getName())) { 534 int componentValue = Integer.parseInt(parser.getAttributeValue(NAMESPACE, "value")); 535 String componentName = getText(parser); 536 customComponentsMap.put(componentName, componentValue); 537 538 if (componentValue < MINIMUM_CUSTOM_COMPONENT_VALUE) { 539 throw new PolicyXmlException( 540 "Invalid custom component value " + componentValue + " " 541 + componentName); 542 } 543 skip(parser); 544 } else { 545 throw new PolicyXmlException( 546 "unknown tag: " + parser.getName() + " under " + TAG_POLICIES); 547 } 548 } 549 return customComponentsMap; 550 } 551 parsePolicies(XmlPullParser parser, boolean includeOtherComponents)552 private List<IntermediateCarPowerPolicy> parsePolicies(XmlPullParser parser, 553 boolean includeOtherComponents) 554 throws PolicyXmlException, XmlPullParserException, IOException { 555 List<IntermediateCarPowerPolicy> policies = new ArrayList<>(); 556 int type; 557 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 558 if (type != START_TAG) continue; 559 if (TAG_POLICY.equals(parser.getName())) { 560 String policyId = parser.getAttributeValue(NAMESPACE, ATTR_ID); 561 if (policyId == null || policyId.equals("")) { 562 throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" + TAG_POLICY 563 + "| tag"); 564 } 565 if (includeOtherComponents && isSystemPowerPolicy(policyId)) { 566 throw new PolicyXmlException("Policy ID should not start with " 567 + SYSTEM_POWER_POLICY_PREFIX); 568 } 569 policies.add(parsePolicy(parser, policyId, includeOtherComponents)); 570 } else { 571 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 572 + TAG_POLICIES); 573 } 574 } 575 return policies; 576 } 577 parsePolicyGroups(XmlPullParser parser)578 private ArrayMap<String, SparseArray<String>> parsePolicyGroups(XmlPullParser parser) throws 579 PolicyXmlException, XmlPullParserException, IOException { 580 ArrayMap<String, SparseArray<String>> policyGroups = new ArrayMap<>(); 581 int type; 582 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 583 if (type != START_TAG) continue; 584 if (TAG_POLICY_GROUP.equals(parser.getName())) { 585 String groupId = parser.getAttributeValue(NAMESPACE, ATTR_ID); 586 if (groupId == null || groupId.equals("")) { 587 throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" 588 + TAG_POLICY_GROUP + "| tag"); 589 } 590 policyGroups.put(groupId, parsePolicyGroup(parser)); 591 } else { 592 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 593 + TAG_POLICY_GROUPS); 594 } 595 } 596 return policyGroups; 597 } 598 parseSystemPolicyOverrides(XmlPullParser parser)599 private List<IntermediateCarPowerPolicy> parseSystemPolicyOverrides(XmlPullParser parser) throws 600 PolicyXmlException, XmlPullParserException, IOException { 601 return parsePolicies(/* parser= */ parser, /* includeOtherComponents= */ false); 602 } 603 @Nullable validateSystemOverrides( List<IntermediateCarPowerPolicy> systemPolicyOverrideIntermediate, ArrayMap<String, Integer> customComponents)604 private CarPowerPolicy validateSystemOverrides( 605 List<IntermediateCarPowerPolicy> systemPolicyOverrideIntermediate, 606 ArrayMap<String, Integer> customComponents) throws PolicyXmlException { 607 int numOverrides = systemPolicyOverrideIntermediate.size(); 608 if (numOverrides == 0) { 609 return null; 610 } 611 if (numOverrides > 1) { 612 throw new PolicyXmlException("only one system power policy is supported: " 613 + numOverrides + " system policies exist"); 614 } 615 if (!systemPolicyOverrideIntermediate.get(0).policyId.equals( 616 POWER_POLICY_ID_NO_USER_INTERACTION)) { 617 throw new PolicyXmlException("system power policy id should be " 618 + POWER_POLICY_ID_NO_USER_INTERACTION); 619 } 620 621 CarPowerPolicy policyOverride = 622 toCarPowerPolicy(systemPolicyOverrideIntermediate.get(0), customComponents); 623 624 Set<Integer> visited = new ArraySet<>(); 625 checkSystemPowerPolicyComponents(policyOverride.getEnabledComponents(), visited); 626 checkSystemPowerPolicyComponents(policyOverride.getDisabledComponents(), visited); 627 return policyOverride; 628 } 629 parsePolicy(XmlPullParser parser, String policyId, boolean includeOtherComponents)630 private IntermediateCarPowerPolicy parsePolicy(XmlPullParser parser, String policyId, 631 boolean includeOtherComponents) 632 throws PolicyXmlException, IOException, XmlPullParserException { 633 ArrayMap<String, Boolean> components = new ArrayMap<>(); 634 String behavior = POWER_ONOFF_UNTOUCHED; 635 boolean otherComponentsProcessed = false; 636 int type; 637 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 638 if (type != START_TAG) continue; 639 if (TAG_COMPONENT.equals(parser.getName())) { 640 String powerComponent = parser.getAttributeValue(NAMESPACE, ATTR_ID); 641 String state = getText(parser); 642 switch (state) { 643 case POWER_ONOFF_ON: 644 components.put(powerComponent, true); 645 break; 646 case POWER_ONOFF_OFF: 647 components.put(powerComponent, false); 648 break; 649 default: 650 throw new PolicyXmlException( 651 "target state(" + state + ") for " + powerComponent 652 + " is not valid"); 653 } 654 skip(parser); 655 } else if (TAG_OTHER_COMPONENTS.equals(parser.getName())) { 656 if (!includeOtherComponents) { 657 throw new PolicyXmlException("|" + TAG_OTHER_COMPONENTS 658 + "| tag is not expected"); 659 } 660 if (otherComponentsProcessed) { 661 throw new PolicyXmlException("more than one |" + TAG_OTHER_COMPONENTS 662 + "| tag"); 663 } 664 otherComponentsProcessed = true; 665 behavior = parser.getAttributeValue(NAMESPACE, ATTR_BEHAVIOR); 666 if (behavior == null) { 667 throw new PolicyXmlException("no |" + ATTR_BEHAVIOR + "| attribute of |" 668 + TAG_OTHER_COMPONENTS + "| tag"); 669 } 670 switch (behavior) { 671 case POWER_ONOFF_ON: 672 case POWER_ONOFF_OFF: 673 case POWER_ONOFF_UNTOUCHED: 674 break; 675 default: 676 throw new PolicyXmlException("invalid value(" + behavior + ") in |" 677 + ATTR_BEHAVIOR + "| attribute of |" + TAG_OTHER_COMPONENTS 678 + "| tag"); 679 } 680 skip(parser); 681 } else { 682 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 683 + TAG_POLICY); 684 } 685 } 686 return new IntermediateCarPowerPolicy(policyId, components, behavior); 687 } 688 toCarPowerPolicy(IntermediateCarPowerPolicy intermediatePolicy, ArrayMap<String, Integer> customComponents)689 private CarPowerPolicy toCarPowerPolicy(IntermediateCarPowerPolicy intermediatePolicy, 690 ArrayMap<String, Integer> customComponents) 691 throws PolicyXmlException { 692 SparseBooleanArray components = new SparseBooleanArray(); 693 694 // Convert string values of IntermediateCarPowerPolicy to a CarPowerPolicy 695 ArrayMap<String, Boolean> intermediatePolicyComponents = intermediatePolicy.components; 696 for (int i = 0; i < intermediatePolicyComponents.size(); i++) { 697 String componentId = intermediatePolicyComponents.keyAt(i); 698 699 int powerComponent = toPowerComponent(componentId, true); 700 if (powerComponent == INVALID_POWER_COMPONENT) { 701 powerComponent = toCustomPowerComponentId(componentId, customComponents); 702 } 703 if (powerComponent == INVALID_POWER_COMPONENT) { 704 throw new PolicyXmlException(" Unknown component id : " + componentId); 705 } 706 707 if (components.indexOfKey(powerComponent) >= 0) { 708 throw new PolicyXmlException( 709 "invalid value(" + componentId + ") in |" + ATTR_ID + "| attribute of |" 710 + TAG_COMPONENT 711 + "| tag"); 712 } 713 components.put(powerComponent, intermediatePolicyComponents.valueAt(i)); 714 } 715 716 boolean enabled; 717 boolean untouched = false; 718 719 if (POWER_ONOFF_ON.equals(intermediatePolicy.otherBehavior)) { 720 enabled = true; 721 } else if (POWER_ONOFF_OFF.equals(intermediatePolicy.otherBehavior)) { 722 enabled = false; 723 } else { 724 enabled = false; 725 untouched = true; 726 } 727 if (!untouched) { 728 for (int component = FIRST_POWER_COMPONENT; 729 component <= LAST_POWER_COMPONENT; component++) { 730 if (components.indexOfKey(component) >= 0) continue; 731 components.put(component, enabled); 732 } 733 for (int i = 0; i < customComponents.size(); ++i) { 734 int componentId = customComponents.valueAt(i); 735 if (components.indexOfKey(componentId) < 0) { // key not found 736 components.put(componentId, enabled); 737 } 738 } 739 } 740 return new CarPowerPolicy(intermediatePolicy.policyId, toIntArray(components, true), 741 toIntArray(components, false)); 742 } 743 toCustomPowerComponentId(String id, ArrayMap<String, Integer> customComponents)744 private int toCustomPowerComponentId(String id, ArrayMap<String, Integer> customComponents) { 745 return customComponents.getOrDefault(id, INVALID_POWER_COMPONENT); 746 } 747 parsePolicyGroup(XmlPullParser parser)748 private SparseArray<String> parsePolicyGroup(XmlPullParser parser) throws PolicyXmlException, 749 XmlPullParserException, IOException { 750 SparseArray<String> policyGroup = new SparseArray<>(); 751 int type; 752 Set<Integer> visited = new ArraySet<>(); 753 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 754 if (type != START_TAG) continue; 755 if (TAG_DEFAULT_POLICY.equals(parser.getName())) { 756 String id = parser.getAttributeValue(NAMESPACE, ATTR_ID); 757 if (id == null || id.isEmpty()) { 758 throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" 759 + TAG_DEFAULT_POLICY + "| tag"); 760 } 761 String state = parser.getAttributeValue(NAMESPACE, ATTR_STATE); 762 int powerState = toPowerState(state); 763 if (powerState == INVALID_POWER_STATE) { 764 throw new PolicyXmlException("invalid value(" + state + ") in |" + ATTR_STATE 765 + "| attribute of |" + TAG_DEFAULT_POLICY + "| tag"); 766 } 767 if (visited.contains(powerState)) { 768 throw new PolicyXmlException("power state(" + state 769 + ") is specified more than once"); 770 } 771 policyGroup.put(powerState, id); 772 visited.add(powerState); 773 skip(parser); 774 } else if (TAG_NO_DEFAULT_POLICY.equals(parser.getName())) { 775 String state = parser.getAttributeValue(NAMESPACE, ATTR_STATE); 776 int powerState = toPowerState(state); 777 if (powerState == INVALID_POWER_STATE) { 778 throw new PolicyXmlException("invalid value(" + state + ") in |" + ATTR_STATE 779 + "| attribute of |" + TAG_DEFAULT_POLICY + "| tag"); 780 } 781 if (visited.contains(powerState)) { 782 throw new PolicyXmlException("power state(" + state 783 + ") is specified more than once"); 784 } 785 visited.add(powerState); 786 skip(parser); 787 } else { 788 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 789 + TAG_POLICY_GROUP); 790 } 791 } 792 return policyGroup; 793 } 794 validatePowerPolicies( List<IntermediateCarPowerPolicy> intermediateCarPowerPolicies, ArrayMap<String, Integer> customComponents)795 private ArrayMap<String, CarPowerPolicy> validatePowerPolicies( 796 List<IntermediateCarPowerPolicy> intermediateCarPowerPolicies, 797 ArrayMap<String, Integer> customComponents) throws PolicyXmlException { 798 ArrayMap<String, CarPowerPolicy> powerPolicies = new ArrayMap<>(); 799 for (int index = 0; index < intermediateCarPowerPolicies.size(); ++index) { 800 IntermediateCarPowerPolicy intermediateCarPowerPolicy = 801 intermediateCarPowerPolicies.get(index); 802 powerPolicies.put(intermediateCarPowerPolicy.policyId, 803 toCarPowerPolicy(intermediateCarPowerPolicy, customComponents)); 804 } 805 return powerPolicies; 806 } 807 validatePolicyGroups(ArrayMap<String, SparseArray<String>> policyGroups, ArrayMap<String, CarPowerPolicy> registeredPolicies, String defaultGroupPolicyId)808 private void validatePolicyGroups(ArrayMap<String, SparseArray<String>> policyGroups, 809 ArrayMap<String, CarPowerPolicy> registeredPolicies, String defaultGroupPolicyId) 810 throws PolicyXmlException { 811 for (Map.Entry<String, SparseArray<String>> entry : policyGroups.entrySet()) { 812 SparseArray<String> group = entry.getValue(); 813 for (int i = 0; i < group.size(); i++) { 814 String policyId = group.valueAt(i); 815 if (!registeredPolicies.containsKey(group.valueAt(i))) { 816 throw new PolicyXmlException("group(id: " + entry.getKey() 817 + ") contains invalid policy(id: " + policyId + ")"); 818 } 819 } 820 } 821 822 if ((defaultGroupPolicyId == null || defaultGroupPolicyId.isEmpty()) 823 && !policyGroups.isEmpty()) { 824 Slogf.w(TAG, "No defaultGroupPolicyId is defined"); 825 } 826 827 if (defaultGroupPolicyId != null && !policyGroups.containsKey(defaultGroupPolicyId)) { 828 throw new PolicyXmlException( 829 "defaultGroupPolicyId is defined, but group with this ID doesn't exist "); 830 } 831 } 832 reconstructSystemPowerPolicy(@ullable CarPowerPolicy policyOverride)833 private void reconstructSystemPowerPolicy(@Nullable CarPowerPolicy policyOverride) { 834 if (policyOverride == null) return; 835 836 List<Integer> enabledComponents = CarServiceUtils.asList( 837 NO_USER_INTERACTION_ENABLED_COMPONENTS); 838 List<Integer> disabledComponents = CarServiceUtils.asList( 839 NO_USER_INTERACTION_DISABLED_COMPONENTS); 840 int[] overrideEnabledComponents = policyOverride.getEnabledComponents(); 841 int[] overrideDisabledComponents = policyOverride.getDisabledComponents(); 842 for (int i = 0; i < overrideEnabledComponents.length; i++) { 843 removeComponent(disabledComponents, overrideEnabledComponents[i]); 844 addComponent(enabledComponents, overrideEnabledComponents[i]); 845 } 846 for (int i = 0; i < overrideDisabledComponents.length; i++) { 847 removeComponent(enabledComponents, overrideDisabledComponents[i]); 848 addComponent(disabledComponents, overrideDisabledComponents[i]); 849 } 850 mPreemptivePowerPolicies.put(POWER_POLICY_ID_NO_USER_INTERACTION, 851 new CarPowerPolicy(POWER_POLICY_ID_NO_USER_INTERACTION, 852 CarServiceUtils.toIntArray(enabledComponents), 853 CarServiceUtils.toIntArray(disabledComponents))); 854 } 855 registerBasicPowerPolicies()856 private void registerBasicPowerPolicies() { 857 mRegisteredPowerPolicies.put(POWER_POLICY_ID_ALL_ON, POWER_POLICY_ALL_ON); 858 mRegisteredPowerPolicies.put(POWER_POLICY_ID_INITIAL_ON, POWER_POLICY_INITIAL_ON); 859 } 860 removeComponent(List<Integer> components, int component)861 private void removeComponent(List<Integer> components, int component) { 862 int index = components.lastIndexOf(component); 863 if (index != -1) { 864 components.remove(index); 865 } 866 } 867 addComponent(List<Integer> components, int component)868 private void addComponent(List<Integer> components, int component) { 869 int index = components.lastIndexOf(component); 870 if (index == -1) { 871 components.add(component); 872 } 873 } 874 getText(XmlPullParser parser)875 private String getText(XmlPullParser parser) throws PolicyXmlException, XmlPullParserException, 876 IOException { 877 if (parser.getEventType() != START_TAG) { 878 throw new PolicyXmlException("tag pair doesn't match"); 879 } 880 parser.next(); 881 if (parser.getEventType() != TEXT) { 882 throw new PolicyXmlException("tag value is not found"); 883 } 884 return parser.getText(); 885 } 886 skip(XmlPullParser parser)887 private void skip(XmlPullParser parser) throws PolicyXmlException, XmlPullParserException, 888 IOException { 889 int type = parser.getEventType(); 890 if (type != START_TAG && type != TEXT) { 891 throw new PolicyXmlException("tag pair doesn't match"); 892 } 893 int depth = 1; 894 while (depth != 0) { 895 switch (parser.next()) { 896 case END_TAG: 897 depth--; 898 break; 899 case START_TAG: 900 depth++; 901 break; 902 default: 903 break; 904 } 905 } 906 } 907 checkSystemPowerPolicyComponents(int[] components, Set<Integer> visited)908 void checkSystemPowerPolicyComponents(int[] components, Set<Integer> visited) throws 909 PolicyXmlException { 910 for (int i = 0; i < components.length; i++) { 911 int component = components[i]; 912 if (!isOverridableComponent(component)) { 913 throw new PolicyXmlException("Power component(" + powerComponentToString(component) 914 + ") cannot be overridden"); 915 } 916 if (visited.contains(component)) { 917 throw new PolicyXmlException("Power component(" + powerComponentToString(component) 918 + ") is specified more than once"); 919 } 920 visited.add(component); 921 } 922 } 923 isOverridableComponent(int component)924 boolean isOverridableComponent(int component) { 925 return component >= MINIMUM_CUSTOM_COMPONENT_VALUE // custom components are overridable 926 || SYSTEM_POLICY_CONFIGURABLE_COMPONENTS.contains(component); 927 } 928 929 @PolicyOperationStatus.ErrorCode parseComponents(String[] componentArr, boolean enabled, SparseBooleanArray components)930 int parseComponents(String[] componentArr, boolean enabled, SparseBooleanArray components) { 931 ArrayList<Integer> customComponentIds = new ArrayList<>(); 932 for (int i = 0; i < componentArr.length; i++) { 933 int component = toPowerComponent(componentArr[i], false); 934 if (component == INVALID_POWER_COMPONENT) { 935 try { 936 component = Integer.parseInt(componentArr[i]); 937 } catch (NumberFormatException e) { 938 Slogf.e(TAG, "Error parsing component ID " + e.toString()); 939 return PolicyOperationStatus.ERROR_INVALID_POWER_COMPONENT; 940 } 941 942 if (component < MINIMUM_CUSTOM_COMPONENT_VALUE) { 943 int error = PolicyOperationStatus.ERROR_INVALID_POWER_COMPONENT; 944 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, componentArr[i])); 945 return error; 946 } 947 } 948 if (components.indexOfKey(component) >= 0) { 949 int error = PolicyOperationStatus.ERROR_DUPLICATED_POWER_COMPONENT; 950 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, componentArr[i])); 951 return error; 952 } 953 components.put(component, enabled); 954 customComponentIds.add(component); 955 } 956 for (int i = 0; i < customComponentIds.size(); ++i) { 957 int componentId = customComponentIds.get(i); 958 // Add only new components 959 if (!mCustomComponents.containsValue(componentId)) { 960 mCustomComponents.put(String.valueOf(componentId), componentId); 961 } 962 } 963 return PolicyOperationStatus.OK; 964 } 965 toPowerState(String state)966 static int toPowerState(String state) { 967 if (state == null) { 968 return INVALID_POWER_STATE; 969 } 970 switch (state) { 971 case POWER_STATE_WAIT_FOR_VHAL: 972 return VehicleApPowerStateReport.WAIT_FOR_VHAL; 973 case POWER_STATE_ON: 974 return VehicleApPowerStateReport.ON; 975 default: 976 return INVALID_POWER_STATE; 977 } 978 } 979 vhalPowerStateToString(int state)980 static String vhalPowerStateToString(int state) { 981 switch (state) { 982 case VehicleApPowerStateReport.WAIT_FOR_VHAL: 983 return POWER_STATE_WAIT_FOR_VHAL; 984 case VehicleApPowerStateReport.ON: 985 return POWER_STATE_ON; 986 default: 987 return "unknown power state"; 988 } 989 } 990 isSystemPowerPolicy(String policyId)991 static boolean isSystemPowerPolicy(String policyId) { 992 return policyId == null ? false : policyId.startsWith(SYSTEM_POWER_POLICY_PREFIX); 993 } 994 toIntArray(SparseBooleanArray array, boolean value)995 private static int[] toIntArray(SparseBooleanArray array, boolean value) { 996 int arraySize = array.size(); 997 int returnSize = 0; 998 for (int i = 0; i < arraySize; i++) { 999 if (array.valueAt(i) == value) returnSize++; 1000 } 1001 int[] ret = new int[returnSize]; 1002 int count = 0; 1003 for (int i = 0; i < arraySize; i++) { 1004 if (array.valueAt(i) == value) { 1005 ret[count++] = array.keyAt(i); 1006 } 1007 } 1008 return ret; 1009 } 1010 containsComponent(int[] arr, int component)1011 private static boolean containsComponent(int[] arr, int component) { 1012 for (int element : arr) { 1013 if (element == component) return true; 1014 } 1015 return false; 1016 } 1017 getCustomComponents()1018 ArrayMap<String, Integer> getCustomComponents() { 1019 return mCustomComponents; 1020 } 1021 1022 @VisibleForTesting getAllComponents()1023 Set<Integer> getAllComponents() { 1024 Set<Integer> allComponents = new ArraySet<>(Lists.asImmutableList(ALL_COMPONENTS)); 1025 allComponents.addAll(mCustomComponents.values()); 1026 return allComponents; 1027 } 1028 1029 @VisibleForTesting 1030 static final class PolicyXmlException extends Exception { PolicyXmlException(String message)1031 PolicyXmlException(String message) { 1032 super(message); 1033 } 1034 } 1035 1036 private static final class IntermediateCarPowerPolicy { 1037 public final String policyId; 1038 public final ArrayMap<String, Boolean> components; 1039 public final String otherBehavior; 1040 IntermediateCarPowerPolicy(String policyId, ArrayMap<String, Boolean> components, String behavior)1041 IntermediateCarPowerPolicy(String policyId, ArrayMap<String, Boolean> components, 1042 String behavior) { 1043 this.policyId = policyId; 1044 this.components = components; 1045 this.otherBehavior = behavior; 1046 } 1047 } 1048 } 1049