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 25 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 26 import static org.xmlpull.v1.XmlPullParser.END_TAG; 27 import static org.xmlpull.v1.XmlPullParser.START_TAG; 28 import static org.xmlpull.v1.XmlPullParser.TEXT; 29 30 import android.annotation.Nullable; 31 import android.car.hardware.power.CarPowerPolicy; 32 import android.car.hardware.power.PowerComponent; 33 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReport; 34 import android.util.ArrayMap; 35 import android.util.ArraySet; 36 import android.util.IndentingPrintWriter; 37 import android.util.SparseArray; 38 import android.util.SparseBooleanArray; 39 import android.util.Xml; 40 41 import com.android.car.CarLog; 42 import com.android.car.CarServiceUtils; 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.server.utils.Slogf; 45 46 import org.xmlpull.v1.XmlPullParser; 47 import org.xmlpull.v1.XmlPullParserException; 48 49 import java.io.FileInputStream; 50 import java.io.IOException; 51 import java.io.InputStream; 52 import java.util.Arrays; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.Set; 56 import java.util.stream.Collectors; 57 58 /** 59 * Helper class to read and manage vendor power policies. 60 * 61 * <p>{@code CarPowerManagementService} manages power policies through {@code PolicyReader}. This 62 * class is not thread-safe, and must be used in the main thread or with additional serialization. 63 */ 64 public final class PolicyReader { 65 public static final String POWER_STATE_WAIT_FOR_VHAL = "WaitForVHAL"; 66 public static final String POWER_STATE_ON = "On"; 67 68 static final String SYSTEM_POWER_POLICY_PREFIX = "system_power_policy_"; 69 // Preemptive system power policy used for disabling user interaction in Silent Mode or Garage 70 // Mode. 71 static final String POWER_POLICY_ID_NO_USER_INTERACTION = SYSTEM_POWER_POLICY_PREFIX 72 + "no_user_interaction"; 73 // Preemptive system power policy used for preparing Suspend-to-RAM. 74 static final String POWER_POLICY_ID_SUSPEND_TO_RAM = SYSTEM_POWER_POLICY_PREFIX 75 + "suspend_to_ram"; 76 // Non-preemptive system power policy used for turning all components on. 77 static final String POWER_POLICY_ID_ALL_ON = SYSTEM_POWER_POLICY_PREFIX + "all_on"; 78 // Non-preemptive system power policy used to represent minimally on state. 79 static final String POWER_POLICY_ID_INITIAL_ON = SYSTEM_POWER_POLICY_PREFIX + "initial_on"; 80 81 static final int INVALID_POWER_STATE = -1; 82 83 private static final String TAG = CarLog.tagFor(PolicyReader.class); 84 private static final String VENDOR_POLICY_PATH = "/vendor/etc/automotive/power_policy.xml"; 85 86 private static final String NAMESPACE = null; 87 private static final Set<String> VALID_VERSIONS = new ArraySet<>(Arrays.asList("1.0")); 88 private static final String TAG_POWER_POLICY = "powerPolicy"; 89 private static final String TAG_POLICY_GROUPS = "policyGroups"; 90 private static final String TAG_POLICY_GROUP = "policyGroup"; 91 private static final String TAG_DEFAULT_POLICY = "defaultPolicy"; 92 private static final String TAG_NO_DEFAULT_POLICY = "noDefaultPolicy"; 93 private static final String TAG_POLICIES = "policies"; 94 private static final String TAG_POLICY = "policy"; 95 private static final String TAG_OTHER_COMPONENTS = "otherComponents"; 96 private static final String TAG_COMPONENT = "component"; 97 private static final String TAG_SYSTEM_POLICY_OVERRIDES = "systemPolicyOverrides"; 98 private static final String ATTR_VERSION = "version"; 99 private static final String ATTR_ID = "id"; 100 private static final String ATTR_STATE = "state"; 101 private static final String ATTR_BEHAVIOR = "behavior"; 102 private static final String POWER_ONOFF_ON = "on"; 103 private static final String POWER_ONOFF_OFF = "off"; 104 private static final String POWER_ONOFF_UNTOUCHED = "untouched"; 105 106 private static final int[] ALL_COMPONENTS; 107 private static final int[] NO_COMPONENTS = new int[0]; 108 private static final int[] INITIAL_ON_COMPONENTS = { 109 PowerComponent.AUDIO, PowerComponent.DISPLAY, PowerComponent.CPU 110 }; 111 private static final int[] NO_USER_INTERACTION_ENABLED_COMPONENTS = { 112 PowerComponent.WIFI, PowerComponent.CELLULAR, 113 PowerComponent.ETHERNET, PowerComponent.TRUSTED_DEVICE_DETECTION, PowerComponent.CPU 114 }; 115 private static final int[] NO_USER_INTERACTION_DISABLED_COMPONENTS = { 116 PowerComponent.AUDIO, PowerComponent.MEDIA, PowerComponent.DISPLAY, 117 PowerComponent.BLUETOOTH, PowerComponent.PROJECTION, PowerComponent.NFC, 118 PowerComponent.INPUT, PowerComponent.VOICE_INTERACTION, 119 PowerComponent.VISUAL_INTERACTION, PowerComponent.LOCATION, PowerComponent.MICROPHONE 120 }; 121 private static final Set<Integer> SYSTEM_POLICY_CONFIGURABLE_COMPONENTS = 122 new ArraySet<>(Arrays.asList(PowerComponent.BLUETOOTH, PowerComponent.NFC, 123 PowerComponent.TRUSTED_DEVICE_DETECTION)); 124 private static final int[] SUSPEND_TO_RAM_DISABLED_COMPONENTS = { 125 PowerComponent.AUDIO, PowerComponent.BLUETOOTH, PowerComponent.WIFI, 126 PowerComponent.LOCATION 127 }; 128 private static final CarPowerPolicy POWER_POLICY_ALL_ON; 129 private static final CarPowerPolicy POWER_POLICY_INITIAL_ON; 130 private static final CarPowerPolicy POWER_POLICY_SUSPEND_TO_RAM; 131 132 static { 133 int allCount = LAST_POWER_COMPONENT - FIRST_POWER_COMPONENT + 1; 134 ALL_COMPONENTS = new int[allCount]; 135 int[] initialOnDisabledComponents = new int[allCount - INITIAL_ON_COMPONENTS.length]; 136 int pos = 0; 137 for (int c = FIRST_POWER_COMPONENT; c <= LAST_POWER_COMPONENT; c++) { 138 ALL_COMPONENTS[c - FIRST_POWER_COMPONENT] = c; 139 if (!containsComponent(INITIAL_ON_COMPONENTS, c)) { 140 initialOnDisabledComponents[pos++] = c; 141 } 142 } 143 144 POWER_POLICY_ALL_ON = new CarPowerPolicy(POWER_POLICY_ID_ALL_ON, ALL_COMPONENTS.clone(), 145 NO_COMPONENTS.clone()); 146 POWER_POLICY_INITIAL_ON = new CarPowerPolicy(POWER_POLICY_ID_INITIAL_ON, 147 INITIAL_ON_COMPONENTS.clone(), initialOnDisabledComponents); 148 POWER_POLICY_SUSPEND_TO_RAM = new CarPowerPolicy(POWER_POLICY_ID_SUSPEND_TO_RAM, 149 NO_COMPONENTS.clone(), SUSPEND_TO_RAM_DISABLED_COMPONENTS.clone()); 150 } 151 152 private ArrayMap<String, CarPowerPolicy> mRegisteredPowerPolicies; 153 private ArrayMap<String, SparseArray<String>> mPolicyGroups; 154 private ArrayMap<String, CarPowerPolicy> mPreemptivePowerPolicies; 155 156 /** 157 * Gets {@code CarPowerPolicy} corresponding to the given policy ID. 158 */ 159 @Nullable getPowerPolicy(String policyId)160 CarPowerPolicy getPowerPolicy(String policyId) { 161 return mRegisteredPowerPolicies.get(policyId); 162 } 163 164 /** 165 * Gets {@code CarPowerPolicy} corresponding to the given power state in the given power 166 * policy group. 167 */ 168 @Nullable getDefaultPowerPolicyForState(String groupId, int state)169 CarPowerPolicy getDefaultPowerPolicyForState(String groupId, int state) { 170 SparseArray<String> group = mPolicyGroups.get(groupId); 171 if (group == null) { 172 return null; 173 } 174 String policyId = group.get(state); 175 if (policyId == null) { 176 return null; 177 } 178 return mRegisteredPowerPolicies.get(policyId); 179 } 180 181 /** 182 * Gets the preemptive power policy corresponding to the given policy ID. 183 * 184 * <p> When a preemptive power policy is the current power policy, applying a regular power 185 * policy is deferred until the preemptive power policy is released. 186 */ 187 @Nullable getPreemptivePowerPolicy(String policyId)188 CarPowerPolicy getPreemptivePowerPolicy(String policyId) { 189 return mPreemptivePowerPolicies.get(policyId); 190 } 191 isPowerPolicyGroupAvailable(String groupId)192 boolean isPowerPolicyGroupAvailable(String groupId) { 193 return mPolicyGroups.containsKey(groupId); 194 } 195 isPreemptivePowerPolicy(String policyId)196 boolean isPreemptivePowerPolicy(String policyId) { 197 return mPreemptivePowerPolicies.containsKey(policyId); 198 } 199 init()200 void init() { 201 initPolicies(); 202 readPowerPolicyConfiguration(); 203 } 204 205 /** 206 * Creates and registers a new power policy. 207 * 208 * @return {@code PolicyOperationStatus.OK}, if successful. Otherwise, the other values. 209 */ 210 @PolicyOperationStatus.ErrorCode definePowerPolicy(String policyId, String[] enabledComponents, String[] disabledComponents)211 int definePowerPolicy(String policyId, String[] enabledComponents, 212 String[] disabledComponents) { 213 if (policyId == null) { 214 int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID; 215 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, "policyId cannot be null")); 216 return error; 217 } 218 if (isSystemPowerPolicy(policyId)) { 219 int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID; 220 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, 221 "policyId should not start with " + SYSTEM_POWER_POLICY_PREFIX)); 222 return error; 223 } 224 if (mRegisteredPowerPolicies.containsKey(policyId)) { 225 int error = PolicyOperationStatus.ERROR_DOUBLE_REGISTERED_POWER_POLICY_ID; 226 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId)); 227 return error; 228 } 229 SparseBooleanArray components = new SparseBooleanArray(); 230 int status = parseComponents(enabledComponents, true, components); 231 if (status != PolicyOperationStatus.OK) { 232 return status; 233 } 234 status = parseComponents(disabledComponents, false, components); 235 if (status != PolicyOperationStatus.OK) { 236 return status; 237 } 238 CarPowerPolicy policy = new CarPowerPolicy(policyId, toIntArray(components, true), 239 toIntArray(components, false)); 240 mRegisteredPowerPolicies.put(policyId, policy); 241 return PolicyOperationStatus.OK; 242 } 243 244 /** 245 * Defines and registers a new power policy group. 246 * 247 * @return {@code PolicyOperationStatus.OK}, if successful. Otherwise, the other values. 248 */ 249 @PolicyOperationStatus.ErrorCode definePowerPolicyGroup(String policyGroupId, SparseArray<String> defaultPolicyPerState)250 int definePowerPolicyGroup(String policyGroupId, SparseArray<String> defaultPolicyPerState) { 251 if (policyGroupId == null) { 252 return PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_GROUP_ID; 253 } 254 if (mPolicyGroups.containsKey(policyGroupId)) { 255 int error = PolicyOperationStatus.ERROR_DOUBLE_REGISTERED_POWER_POLICY_GROUP_ID; 256 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyGroupId)); 257 return error; 258 } 259 for (int i = 0; i < defaultPolicyPerState.size(); i++) { 260 int state = defaultPolicyPerState.keyAt(i); 261 String policyId = defaultPolicyPerState.valueAt(i); 262 if (!mRegisteredPowerPolicies.containsKey(policyId)) { 263 int error = PolicyOperationStatus.ERROR_NOT_REGISTERED_POWER_POLICY_ID; 264 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId + " for " 265 + powerStateToString(state))); 266 return error; 267 } 268 } 269 mPolicyGroups.put(policyGroupId, defaultPolicyPerState); 270 return PolicyOperationStatus.OK; 271 } 272 dump(IndentingPrintWriter writer)273 void dump(IndentingPrintWriter writer) { 274 writer.printf("Registered power policies:%s\n", 275 mRegisteredPowerPolicies.size() == 0 ? " none" : ""); 276 writer.increaseIndent(); 277 for (Map.Entry<String, CarPowerPolicy> entry : mRegisteredPowerPolicies.entrySet()) { 278 writer.println(toString(entry.getValue())); 279 } 280 writer.decreaseIndent(); 281 writer.printf("Power policy groups:%s\n", mPolicyGroups.isEmpty() ? " none" : ""); 282 writer.increaseIndent(); 283 for (Map.Entry<String, SparseArray<String>> entry : mPolicyGroups.entrySet()) { 284 writer.printf("%s\n", entry.getKey()); 285 writer.increaseIndent(); 286 SparseArray<String> group = entry.getValue(); 287 for (int i = 0; i < group.size(); i++) { 288 writer.printf("- %s --> %s\n", powerStateToString(group.keyAt(i)), 289 group.valueAt(i)); 290 } 291 writer.decreaseIndent(); 292 } 293 writer.decreaseIndent(); 294 writer.println("Preemptive power policy:"); 295 writer.increaseIndent(); 296 for (int i = 0; i < mPreemptivePowerPolicies.size(); i++) { 297 writer.println(toString(mPreemptivePowerPolicies.valueAt(i))); 298 } 299 writer.decreaseIndent(); 300 } 301 302 @VisibleForTesting initPolicies()303 void initPolicies() { 304 mRegisteredPowerPolicies = new ArrayMap<>(); 305 mRegisteredPowerPolicies.put(POWER_POLICY_ID_ALL_ON, POWER_POLICY_ALL_ON); 306 mRegisteredPowerPolicies.put(POWER_POLICY_ID_INITIAL_ON, POWER_POLICY_INITIAL_ON); 307 308 mPolicyGroups = new ArrayMap<>(); 309 310 mPreemptivePowerPolicies = new ArrayMap<>(); 311 mPreemptivePowerPolicies.put(POWER_POLICY_ID_NO_USER_INTERACTION, 312 new CarPowerPolicy(POWER_POLICY_ID_NO_USER_INTERACTION, 313 NO_USER_INTERACTION_ENABLED_COMPONENTS.clone(), 314 NO_USER_INTERACTION_DISABLED_COMPONENTS.clone())); 315 mPreemptivePowerPolicies.put(POWER_POLICY_ID_SUSPEND_TO_RAM, POWER_POLICY_SUSPEND_TO_RAM); 316 } 317 readPowerPolicyConfiguration()318 private void readPowerPolicyConfiguration() { 319 try (InputStream inputStream = new FileInputStream(VENDOR_POLICY_PATH)) { 320 readPowerPolicyFromXml(inputStream); 321 } catch (IOException | XmlPullParserException | PolicyXmlException e) { 322 Slogf.w(TAG, "Proceed without registered policies: failed to parse %s: %s", 323 VENDOR_POLICY_PATH, e); 324 } 325 } 326 327 @VisibleForTesting readPowerPolicyFromXml(InputStream stream)328 void readPowerPolicyFromXml(InputStream stream) throws PolicyXmlException, 329 XmlPullParserException, IOException { 330 XmlPullParser parser = Xml.newPullParser(); 331 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, NAMESPACE != null); 332 parser.setInput(stream, null); 333 334 // Ensure <powerPolicy> is the root 335 parser.nextTag(); 336 parser.require(START_TAG, NAMESPACE, TAG_POWER_POLICY); 337 // Check version 338 String version = parser.getAttributeValue(NAMESPACE, ATTR_VERSION); 339 if (!VALID_VERSIONS.contains(version)) { 340 throw new PolicyXmlException("invalid XML version: " + version); 341 } 342 343 ArrayMap<String, CarPowerPolicy> registeredPolicies = new ArrayMap<>(); 344 ArrayMap<String, SparseArray<String>> policyGroups = new ArrayMap<>(); 345 CarPowerPolicy systemPolicyOverride = null; 346 347 int type; 348 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 349 if (type != START_TAG) continue; 350 switch (parser.getName()) { 351 case TAG_POLICIES: 352 registeredPolicies = parsePolicies(parser, true); 353 break; 354 case TAG_POLICY_GROUPS: 355 policyGroups = parsePolicyGroups(parser); 356 break; 357 case TAG_SYSTEM_POLICY_OVERRIDES: 358 systemPolicyOverride = parseSystemPolicyOverrides(parser); 359 break; 360 default: 361 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 362 + TAG_POWER_POLICY); 363 } 364 } 365 validatePolicyGroups(policyGroups, registeredPolicies); 366 367 mRegisteredPowerPolicies = registeredPolicies; 368 mPolicyGroups = policyGroups; 369 reconstructSystemPowerPolicy(systemPolicyOverride); 370 } 371 parsePolicies(XmlPullParser parser, boolean includeOtherComponents)372 private ArrayMap<String, CarPowerPolicy> parsePolicies(XmlPullParser parser, 373 boolean includeOtherComponents) throws PolicyXmlException, XmlPullParserException, 374 IOException { 375 ArrayMap<String, CarPowerPolicy> policies = new ArrayMap<>(); 376 int type; 377 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 378 if (type != START_TAG) continue; 379 if (TAG_POLICY.equals(parser.getName())) { 380 String policyId = parser.getAttributeValue(NAMESPACE, ATTR_ID); 381 if (policyId == null || policyId.equals("")) { 382 throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" + TAG_POLICY 383 + "| tag"); 384 } 385 if (includeOtherComponents && isSystemPowerPolicy(policyId)) { 386 throw new PolicyXmlException("Policy ID should not start with " 387 + SYSTEM_POWER_POLICY_PREFIX); 388 } 389 policies.put(policyId, parsePolicy(parser, policyId, includeOtherComponents)); 390 } else { 391 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 392 + TAG_POLICIES); 393 } 394 } 395 return policies; 396 } 397 parsePolicyGroups(XmlPullParser parser)398 private ArrayMap<String, SparseArray<String>> parsePolicyGroups(XmlPullParser parser) throws 399 PolicyXmlException, XmlPullParserException, IOException { 400 ArrayMap<String, SparseArray<String>> policyGroups = new ArrayMap<>(); 401 int type; 402 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 403 if (type != START_TAG) continue; 404 if (TAG_POLICY_GROUP.equals(parser.getName())) { 405 String groupId = parser.getAttributeValue(NAMESPACE, ATTR_ID); 406 if (groupId == null || groupId.equals("")) { 407 throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" 408 + TAG_POLICY_GROUP + "| tag"); 409 } 410 policyGroups.put(groupId, parsePolicyGroup(parser)); 411 } else { 412 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 413 + TAG_POLICY_GROUPS); 414 } 415 } 416 return policyGroups; 417 } 418 419 @Nullable parseSystemPolicyOverrides(XmlPullParser parser)420 private CarPowerPolicy parseSystemPolicyOverrides(XmlPullParser parser) throws 421 PolicyXmlException, XmlPullParserException, IOException { 422 ArrayMap<String, CarPowerPolicy> systemOverrides = parsePolicies(parser, false); 423 int numOverrides = systemOverrides.size(); 424 if (numOverrides == 0) { 425 return null; 426 } 427 if (numOverrides > 1) { 428 throw new PolicyXmlException("only one system power policy is supported: " 429 + numOverrides + " system policies exist"); 430 } 431 CarPowerPolicy policyOverride = 432 systemOverrides.get(POWER_POLICY_ID_NO_USER_INTERACTION); 433 if (policyOverride == null) { 434 throw new PolicyXmlException("system power policy id should be " 435 + POWER_POLICY_ID_NO_USER_INTERACTION); 436 } 437 Set<Integer> visited = new ArraySet<>(); 438 checkSystemPowerPolicyComponents(policyOverride.getEnabledComponents(), visited); 439 checkSystemPowerPolicyComponents(policyOverride.getDisabledComponents(), visited); 440 return policyOverride; 441 } 442 parsePolicy(XmlPullParser parser, String policyId, boolean includeOtherComponents)443 private CarPowerPolicy parsePolicy(XmlPullParser parser, String policyId, 444 boolean includeOtherComponents) throws PolicyXmlException, XmlPullParserException, 445 IOException { 446 SparseBooleanArray components = new SparseBooleanArray(); 447 String behavior = POWER_ONOFF_UNTOUCHED; 448 boolean otherComponentsProcessed = false; 449 int type; 450 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 451 if (type != START_TAG) continue; 452 if (TAG_COMPONENT.equals(parser.getName())) { 453 String id = parser.getAttributeValue(NAMESPACE, ATTR_ID); 454 int powerComponent = toPowerComponent(id, true); 455 if (powerComponent == INVALID_POWER_COMPONENT) { 456 throw new PolicyXmlException("invalid value(" + id + ") in |" + ATTR_ID 457 + "| attribute of |" + TAG_COMPONENT + "| tag"); 458 } 459 if (components.indexOfKey(powerComponent) >= 0) { 460 throw new PolicyXmlException(id + " is specified more than once in |" 461 + TAG_COMPONENT + "| tag"); 462 } 463 String state = getText(parser); 464 switch (state) { 465 case POWER_ONOFF_ON: 466 components.put(powerComponent, true); 467 break; 468 case POWER_ONOFF_OFF: 469 components.put(powerComponent, false); 470 break; 471 default: 472 throw new PolicyXmlException("target state(" + state + ") for " + id 473 + " is not valid"); 474 } 475 skip(parser); 476 } else if (TAG_OTHER_COMPONENTS.equals(parser.getName())) { 477 if (!includeOtherComponents) { 478 throw new PolicyXmlException("|" + TAG_OTHER_COMPONENTS 479 + "| tag is not expected"); 480 } 481 if (otherComponentsProcessed) { 482 throw new PolicyXmlException("more than one |" + TAG_OTHER_COMPONENTS 483 + "| tag"); 484 } 485 otherComponentsProcessed = true; 486 behavior = parser.getAttributeValue(NAMESPACE, ATTR_BEHAVIOR); 487 if (behavior == null) { 488 throw new PolicyXmlException("no |" + ATTR_BEHAVIOR + "| attribute of |" 489 + TAG_OTHER_COMPONENTS + "| tag"); 490 } 491 switch (behavior) { 492 case POWER_ONOFF_ON: 493 case POWER_ONOFF_OFF: 494 case POWER_ONOFF_UNTOUCHED: 495 break; 496 default: 497 throw new PolicyXmlException("invalid value(" + behavior + ") in |" 498 + ATTR_BEHAVIOR + "| attribute of |" + TAG_OTHER_COMPONENTS 499 + "| tag"); 500 } 501 skip(parser); 502 } else { 503 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 504 + TAG_POLICY); 505 } 506 } 507 boolean enabled = false; 508 boolean untouched = false; 509 if (POWER_ONOFF_ON.equals(behavior)) { 510 enabled = true; 511 } else if (POWER_ONOFF_OFF.equals(behavior)) { 512 enabled = false; 513 } else { 514 untouched = true; 515 } 516 if (!untouched) { 517 for (int component = FIRST_POWER_COMPONENT; 518 component <= LAST_POWER_COMPONENT; component++) { 519 if (components.indexOfKey(component) >= 0) continue; 520 components.put(component, enabled); 521 } 522 } 523 return new CarPowerPolicy(policyId, toIntArray(components, true), 524 toIntArray(components, false)); 525 } 526 parsePolicyGroup(XmlPullParser parser)527 private SparseArray<String> parsePolicyGroup(XmlPullParser parser) throws PolicyXmlException, 528 XmlPullParserException, IOException { 529 SparseArray<String> policyGroup = new SparseArray<>(); 530 int type; 531 Set<Integer> visited = new ArraySet<>(); 532 while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) { 533 if (type != START_TAG) continue; 534 if (TAG_DEFAULT_POLICY.equals(parser.getName())) { 535 String id = parser.getAttributeValue(NAMESPACE, ATTR_ID); 536 if (id == null || id.isEmpty()) { 537 throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" 538 + TAG_DEFAULT_POLICY + "| tag"); 539 } 540 String state = parser.getAttributeValue(NAMESPACE, ATTR_STATE); 541 int powerState = toPowerState(state); 542 if (powerState == INVALID_POWER_STATE) { 543 throw new PolicyXmlException("invalid value(" + state + ") in |" + ATTR_STATE 544 + "| attribute of |" + TAG_DEFAULT_POLICY + "| tag"); 545 } 546 if (visited.contains(powerState)) { 547 throw new PolicyXmlException("power state(" + state 548 + ") is specified more than once"); 549 } 550 policyGroup.put(powerState, id); 551 visited.add(powerState); 552 skip(parser); 553 } else if (TAG_NO_DEFAULT_POLICY.equals(parser.getName())) { 554 String state = parser.getAttributeValue(NAMESPACE, ATTR_STATE); 555 int powerState = toPowerState(state); 556 if (powerState == INVALID_POWER_STATE) { 557 throw new PolicyXmlException("invalid value(" + state + ") in |" + ATTR_STATE 558 + "| attribute of |" + TAG_DEFAULT_POLICY + "| tag"); 559 } 560 if (visited.contains(powerState)) { 561 throw new PolicyXmlException("power state(" + state 562 + ") is specified more than once"); 563 } 564 visited.add(powerState); 565 skip(parser); 566 } else { 567 throw new PolicyXmlException("unknown tag: " + parser.getName() + " under " 568 + TAG_POLICY_GROUP); 569 } 570 } 571 return policyGroup; 572 } 573 validatePolicyGroups(ArrayMap<String, SparseArray<String>> policyGroups, ArrayMap<String, CarPowerPolicy> registeredPolicies)574 private void validatePolicyGroups(ArrayMap<String, SparseArray<String>> policyGroups, 575 ArrayMap<String, CarPowerPolicy> registeredPolicies) throws PolicyXmlException { 576 for (Map.Entry<String, SparseArray<String>> entry : policyGroups.entrySet()) { 577 SparseArray<String> group = entry.getValue(); 578 for (int i = 0; i < group.size(); i++) { 579 String policyId = group.valueAt(i); 580 if (!registeredPolicies.containsKey(group.valueAt(i))) { 581 throw new PolicyXmlException("group(id: " + entry.getKey() 582 + ") contains invalid policy(id: " + policyId + ")"); 583 } 584 } 585 } 586 } 587 reconstructSystemPowerPolicy(@ullable CarPowerPolicy policyOverride)588 private void reconstructSystemPowerPolicy(@Nullable CarPowerPolicy policyOverride) { 589 if (policyOverride == null) return; 590 591 List<Integer> enabledComponents = Arrays.stream(NO_USER_INTERACTION_ENABLED_COMPONENTS) 592 .boxed().collect(Collectors.toList()); 593 List<Integer> disabledComponents = Arrays.stream(NO_USER_INTERACTION_DISABLED_COMPONENTS) 594 .boxed().collect(Collectors.toList()); 595 int[] overrideEnabledComponents = policyOverride.getEnabledComponents(); 596 int[] overrideDisabledComponents = policyOverride.getDisabledComponents(); 597 for (int i = 0; i < overrideEnabledComponents.length; i++) { 598 removeComponent(disabledComponents, overrideEnabledComponents[i]); 599 addComponent(enabledComponents, overrideEnabledComponents[i]); 600 } 601 for (int i = 0; i < overrideDisabledComponents.length; i++) { 602 removeComponent(enabledComponents, overrideDisabledComponents[i]); 603 addComponent(disabledComponents, overrideDisabledComponents[i]); 604 } 605 mPreemptivePowerPolicies.put(POWER_POLICY_ID_NO_USER_INTERACTION, 606 new CarPowerPolicy(POWER_POLICY_ID_NO_USER_INTERACTION, 607 CarServiceUtils.toIntArray(enabledComponents), 608 CarServiceUtils.toIntArray(disabledComponents))); 609 } 610 removeComponent(List<Integer> components, int component)611 private void removeComponent(List<Integer> components, int component) { 612 int index = components.lastIndexOf(component); 613 if (index != -1) { 614 components.remove(index); 615 } 616 } 617 addComponent(List<Integer> components, int component)618 private void addComponent(List<Integer> components, int component) { 619 int index = components.lastIndexOf(component); 620 if (index == -1) { 621 components.add(component); 622 } 623 } 624 getText(XmlPullParser parser)625 private String getText(XmlPullParser parser) throws PolicyXmlException, XmlPullParserException, 626 IOException { 627 if (parser.getEventType() != START_TAG) { 628 throw new PolicyXmlException("tag pair doesn't match"); 629 } 630 parser.next(); 631 if (parser.getEventType() != TEXT) { 632 throw new PolicyXmlException("tag value is not found"); 633 } 634 return parser.getText(); 635 } 636 skip(XmlPullParser parser)637 private void skip(XmlPullParser parser) throws PolicyXmlException, XmlPullParserException, 638 IOException { 639 int type = parser.getEventType(); 640 if (type != START_TAG && type != TEXT) { 641 throw new PolicyXmlException("tag pair doesn't match"); 642 } 643 int depth = 1; 644 while (depth != 0) { 645 switch (parser.next()) { 646 case END_TAG: 647 depth--; 648 break; 649 case START_TAG: 650 depth++; 651 break; 652 } 653 } 654 } 655 checkSystemPowerPolicyComponents(int[] components, Set<Integer> visited)656 void checkSystemPowerPolicyComponents(int[] components, Set<Integer> visited) throws 657 PolicyXmlException { 658 for (int i = 0; i < components.length; i++) { 659 int component = components[i]; 660 if (!isOverridableComponent(component)) { 661 throw new PolicyXmlException("Power component(" + powerComponentToString(component) 662 + ") cannot be overridden"); 663 } 664 if (visited.contains(component)) { 665 throw new PolicyXmlException("Power component(" + powerComponentToString(component) 666 + ") is specified more than once"); 667 } 668 visited.add(component); 669 } 670 } 671 isOverridableComponent(int component)672 boolean isOverridableComponent(int component) { 673 return SYSTEM_POLICY_CONFIGURABLE_COMPONENTS.contains(component); 674 } 675 toString(CarPowerPolicy policy)676 private String toString(CarPowerPolicy policy) { 677 return policy.getPolicyId() + "(enabledComponents: " 678 + componentsToString(policy.getEnabledComponents()) + " | disabledComponents: " 679 + componentsToString(policy.getDisabledComponents()) + ")"; 680 } 681 componentsToString(int[] components)682 private String componentsToString(int[] components) { 683 StringBuffer buffer = new StringBuffer(); 684 for (int i = 0; i < components.length; i++) { 685 if (i > 0) buffer.append(", "); 686 buffer.append(powerComponentToString(components[i])); 687 } 688 return buffer.toString(); 689 } 690 691 @PolicyOperationStatus.ErrorCode parseComponents(String[] componentArr, boolean enabled, SparseBooleanArray components)692 int parseComponents(String[] componentArr, boolean enabled, SparseBooleanArray components) { 693 for (int i = 0; i < componentArr.length; i++) { 694 int component = toPowerComponent(componentArr[i], false); 695 if (component == INVALID_POWER_COMPONENT) { 696 int error = PolicyOperationStatus.ERROR_INVALID_POWER_COMPONENT; 697 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, componentArr[i])); 698 return error; 699 } 700 if (components.indexOfKey(component) >= 0) { 701 int error = PolicyOperationStatus.ERROR_DUPLICATED_POWER_COMPONENT; 702 Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, componentArr[i])); 703 return error; 704 } 705 components.put(component, enabled); 706 } 707 return PolicyOperationStatus.OK; 708 } 709 toPowerState(String state)710 static int toPowerState(String state) { 711 if (state == null) { 712 return INVALID_POWER_STATE; 713 } 714 switch (state) { 715 case POWER_STATE_WAIT_FOR_VHAL: 716 return VehicleApPowerStateReport.WAIT_FOR_VHAL; 717 case POWER_STATE_ON: 718 return VehicleApPowerStateReport.ON; 719 default: 720 return INVALID_POWER_STATE; 721 } 722 } 723 powerStateToString(int state)724 static String powerStateToString(int state) { 725 switch (state) { 726 case VehicleApPowerStateReport.WAIT_FOR_VHAL: 727 return POWER_STATE_WAIT_FOR_VHAL; 728 case VehicleApPowerStateReport.ON: 729 return POWER_STATE_ON; 730 default: 731 return "unknown power state"; 732 } 733 } 734 isSystemPowerPolicy(String policyId)735 static boolean isSystemPowerPolicy(String policyId) { 736 return policyId == null ? false : policyId.startsWith(SYSTEM_POWER_POLICY_PREFIX); 737 } 738 toIntArray(SparseBooleanArray array, boolean value)739 private static int[] toIntArray(SparseBooleanArray array, boolean value) { 740 int arraySize = array.size(); 741 int returnSize = 0; 742 for (int i = 0; i < arraySize; i++) { 743 if (array.valueAt(i) == value) returnSize++; 744 } 745 int[] ret = new int[returnSize]; 746 int count = 0; 747 for (int i = 0; i < arraySize; i++) { 748 if (array.valueAt(i) == value) { 749 ret[count++] = array.keyAt(i); 750 } 751 } 752 return ret; 753 } 754 containsComponent(int[] arr, int component)755 private static boolean containsComponent(int[] arr, int component) { 756 for (int element : arr) { 757 if (element == component) return true; 758 } 759 return false; 760 } 761 762 @VisibleForTesting 763 static final class PolicyXmlException extends Exception { PolicyXmlException(String message)764 PolicyXmlException(String message) { 765 super(message); 766 } 767 } 768 } 769