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