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