/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.power;
import static android.car.hardware.power.PowerComponentUtil.FIRST_POWER_COMPONENT;
import static android.car.hardware.power.PowerComponentUtil.INVALID_POWER_COMPONENT;
import static android.car.hardware.power.PowerComponentUtil.LAST_POWER_COMPONENT;
import static android.car.hardware.power.PowerComponentUtil.powerComponentToString;
import static android.car.hardware.power.PowerComponentUtil.toPowerComponent;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
import android.annotation.Nullable;
import android.car.hardware.power.CarPowerPolicy;
import android.car.hardware.power.PowerComponent;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReport;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.Xml;
import com.android.car.CarLog;
import com.android.car.CarServiceUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.utils.Slogf;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Helper class to read and manage vendor power policies.
*
*
{@code CarPowerManagementService} manages power policies through {@code PolicyReader}. This
* class is not thread-safe, and must be used in the main thread or with additional serialization.
*/
public final class PolicyReader {
public static final String POWER_STATE_WAIT_FOR_VHAL = "WaitForVHAL";
public static final String POWER_STATE_ON = "On";
static final String SYSTEM_POWER_POLICY_PREFIX = "system_power_policy_";
// Preemptive system power policy used for disabling user interaction in Silent Mode or Garage
// Mode.
static final String POWER_POLICY_ID_NO_USER_INTERACTION = SYSTEM_POWER_POLICY_PREFIX
+ "no_user_interaction";
// Preemptive system power policy used for preparing Suspend-to-RAM.
static final String POWER_POLICY_ID_SUSPEND_TO_RAM = SYSTEM_POWER_POLICY_PREFIX
+ "suspend_to_ram";
// Non-preemptive system power policy used for turning all components on.
static final String POWER_POLICY_ID_ALL_ON = SYSTEM_POWER_POLICY_PREFIX + "all_on";
// Non-preemptive system power policy used to represent minimally on state.
static final String POWER_POLICY_ID_INITIAL_ON = SYSTEM_POWER_POLICY_PREFIX + "initial_on";
static final int INVALID_POWER_STATE = -1;
private static final String TAG = CarLog.tagFor(PolicyReader.class);
private static final String VENDOR_POLICY_PATH = "/vendor/etc/automotive/power_policy.xml";
private static final String NAMESPACE = null;
private static final Set VALID_VERSIONS = new ArraySet<>(Arrays.asList("1.0"));
private static final String TAG_POWER_POLICY = "powerPolicy";
private static final String TAG_POLICY_GROUPS = "policyGroups";
private static final String TAG_POLICY_GROUP = "policyGroup";
private static final String TAG_DEFAULT_POLICY = "defaultPolicy";
private static final String TAG_NO_DEFAULT_POLICY = "noDefaultPolicy";
private static final String TAG_POLICIES = "policies";
private static final String TAG_POLICY = "policy";
private static final String TAG_OTHER_COMPONENTS = "otherComponents";
private static final String TAG_COMPONENT = "component";
private static final String TAG_SYSTEM_POLICY_OVERRIDES = "systemPolicyOverrides";
private static final String ATTR_VERSION = "version";
private static final String ATTR_ID = "id";
private static final String ATTR_STATE = "state";
private static final String ATTR_BEHAVIOR = "behavior";
private static final String POWER_ONOFF_ON = "on";
private static final String POWER_ONOFF_OFF = "off";
private static final String POWER_ONOFF_UNTOUCHED = "untouched";
private static final int[] ALL_COMPONENTS;
private static final int[] NO_COMPONENTS = new int[0];
private static final int[] INITIAL_ON_COMPONENTS = {
PowerComponent.AUDIO, PowerComponent.DISPLAY, PowerComponent.CPU
};
private static final int[] NO_USER_INTERACTION_ENABLED_COMPONENTS = {
PowerComponent.WIFI, PowerComponent.CELLULAR,
PowerComponent.ETHERNET, PowerComponent.TRUSTED_DEVICE_DETECTION, PowerComponent.CPU
};
private static final int[] NO_USER_INTERACTION_DISABLED_COMPONENTS = {
PowerComponent.AUDIO, PowerComponent.MEDIA, PowerComponent.DISPLAY,
PowerComponent.BLUETOOTH, PowerComponent.PROJECTION, PowerComponent.NFC,
PowerComponent.INPUT, PowerComponent.VOICE_INTERACTION,
PowerComponent.VISUAL_INTERACTION, PowerComponent.LOCATION, PowerComponent.MICROPHONE
};
private static final Set SYSTEM_POLICY_CONFIGURABLE_COMPONENTS =
new ArraySet<>(Arrays.asList(PowerComponent.BLUETOOTH, PowerComponent.NFC,
PowerComponent.TRUSTED_DEVICE_DETECTION));
private static final int[] SUSPEND_TO_RAM_DISABLED_COMPONENTS = {
PowerComponent.AUDIO, PowerComponent.BLUETOOTH, PowerComponent.WIFI,
PowerComponent.LOCATION
};
private static final CarPowerPolicy POWER_POLICY_ALL_ON;
private static final CarPowerPolicy POWER_POLICY_INITIAL_ON;
private static final CarPowerPolicy POWER_POLICY_SUSPEND_TO_RAM;
static {
int allCount = LAST_POWER_COMPONENT - FIRST_POWER_COMPONENT + 1;
ALL_COMPONENTS = new int[allCount];
int[] initialOnDisabledComponents = new int[allCount - INITIAL_ON_COMPONENTS.length];
int pos = 0;
for (int c = FIRST_POWER_COMPONENT; c <= LAST_POWER_COMPONENT; c++) {
ALL_COMPONENTS[c - FIRST_POWER_COMPONENT] = c;
if (!containsComponent(INITIAL_ON_COMPONENTS, c)) {
initialOnDisabledComponents[pos++] = c;
}
}
POWER_POLICY_ALL_ON = new CarPowerPolicy(POWER_POLICY_ID_ALL_ON, ALL_COMPONENTS.clone(),
NO_COMPONENTS.clone());
POWER_POLICY_INITIAL_ON = new CarPowerPolicy(POWER_POLICY_ID_INITIAL_ON,
INITIAL_ON_COMPONENTS.clone(), initialOnDisabledComponents);
POWER_POLICY_SUSPEND_TO_RAM = new CarPowerPolicy(POWER_POLICY_ID_SUSPEND_TO_RAM,
NO_COMPONENTS.clone(), SUSPEND_TO_RAM_DISABLED_COMPONENTS.clone());
}
private ArrayMap mRegisteredPowerPolicies;
private ArrayMap> mPolicyGroups;
private ArrayMap mPreemptivePowerPolicies;
/**
* Gets {@code CarPowerPolicy} corresponding to the given policy ID.
*/
@Nullable
CarPowerPolicy getPowerPolicy(String policyId) {
return mRegisteredPowerPolicies.get(policyId);
}
/**
* Gets {@code CarPowerPolicy} corresponding to the given power state in the given power
* policy group.
*/
@Nullable
CarPowerPolicy getDefaultPowerPolicyForState(String groupId, int state) {
SparseArray group = mPolicyGroups.get(groupId);
if (group == null) {
return null;
}
String policyId = group.get(state);
if (policyId == null) {
return null;
}
return mRegisteredPowerPolicies.get(policyId);
}
/**
* Gets the preemptive power policy corresponding to the given policy ID.
*
* When a preemptive power policy is the current power policy, applying a regular power
* policy is deferred until the preemptive power policy is released.
*/
@Nullable
CarPowerPolicy getPreemptivePowerPolicy(String policyId) {
return mPreemptivePowerPolicies.get(policyId);
}
boolean isPowerPolicyGroupAvailable(String groupId) {
return mPolicyGroups.containsKey(groupId);
}
boolean isPreemptivePowerPolicy(String policyId) {
return mPreemptivePowerPolicies.containsKey(policyId);
}
void init() {
initPolicies();
readPowerPolicyConfiguration();
}
/**
* Creates and registers a new power policy.
*
* @return {@code PolicyOperationStatus.OK}, if successful. Otherwise, the other values.
*/
@PolicyOperationStatus.ErrorCode
int definePowerPolicy(String policyId, String[] enabledComponents,
String[] disabledComponents) {
if (policyId == null) {
int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, "policyId cannot be null"));
return error;
}
if (isSystemPowerPolicy(policyId)) {
int error = PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_ID;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error,
"policyId should not start with " + SYSTEM_POWER_POLICY_PREFIX));
return error;
}
if (mRegisteredPowerPolicies.containsKey(policyId)) {
int error = PolicyOperationStatus.ERROR_DOUBLE_REGISTERED_POWER_POLICY_ID;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId));
return error;
}
SparseBooleanArray components = new SparseBooleanArray();
int status = parseComponents(enabledComponents, true, components);
if (status != PolicyOperationStatus.OK) {
return status;
}
status = parseComponents(disabledComponents, false, components);
if (status != PolicyOperationStatus.OK) {
return status;
}
CarPowerPolicy policy = new CarPowerPolicy(policyId, toIntArray(components, true),
toIntArray(components, false));
mRegisteredPowerPolicies.put(policyId, policy);
return PolicyOperationStatus.OK;
}
/**
* Defines and registers a new power policy group.
*
* @return {@code PolicyOperationStatus.OK}, if successful. Otherwise, the other values.
*/
@PolicyOperationStatus.ErrorCode
int definePowerPolicyGroup(String policyGroupId, SparseArray defaultPolicyPerState) {
if (policyGroupId == null) {
return PolicyOperationStatus.ERROR_INVALID_POWER_POLICY_GROUP_ID;
}
if (mPolicyGroups.containsKey(policyGroupId)) {
int error = PolicyOperationStatus.ERROR_DOUBLE_REGISTERED_POWER_POLICY_GROUP_ID;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyGroupId));
return error;
}
for (int i = 0; i < defaultPolicyPerState.size(); i++) {
int state = defaultPolicyPerState.keyAt(i);
String policyId = defaultPolicyPerState.valueAt(i);
if (!mRegisteredPowerPolicies.containsKey(policyId)) {
int error = PolicyOperationStatus.ERROR_NOT_REGISTERED_POWER_POLICY_ID;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, policyId + " for "
+ powerStateToString(state)));
return error;
}
}
mPolicyGroups.put(policyGroupId, defaultPolicyPerState);
return PolicyOperationStatus.OK;
}
void dump(IndentingPrintWriter writer) {
writer.printf("Registered power policies:%s\n",
mRegisteredPowerPolicies.size() == 0 ? " none" : "");
writer.increaseIndent();
for (Map.Entry entry : mRegisteredPowerPolicies.entrySet()) {
writer.println(toString(entry.getValue()));
}
writer.decreaseIndent();
writer.printf("Power policy groups:%s\n", mPolicyGroups.isEmpty() ? " none" : "");
writer.increaseIndent();
for (Map.Entry> entry : mPolicyGroups.entrySet()) {
writer.printf("%s\n", entry.getKey());
writer.increaseIndent();
SparseArray group = entry.getValue();
for (int i = 0; i < group.size(); i++) {
writer.printf("- %s --> %s\n", powerStateToString(group.keyAt(i)),
group.valueAt(i));
}
writer.decreaseIndent();
}
writer.decreaseIndent();
writer.println("Preemptive power policy:");
writer.increaseIndent();
for (int i = 0; i < mPreemptivePowerPolicies.size(); i++) {
writer.println(toString(mPreemptivePowerPolicies.valueAt(i)));
}
writer.decreaseIndent();
}
@VisibleForTesting
void initPolicies() {
mRegisteredPowerPolicies = new ArrayMap<>();
mRegisteredPowerPolicies.put(POWER_POLICY_ID_ALL_ON, POWER_POLICY_ALL_ON);
mRegisteredPowerPolicies.put(POWER_POLICY_ID_INITIAL_ON, POWER_POLICY_INITIAL_ON);
mPolicyGroups = new ArrayMap<>();
mPreemptivePowerPolicies = new ArrayMap<>();
mPreemptivePowerPolicies.put(POWER_POLICY_ID_NO_USER_INTERACTION,
new CarPowerPolicy(POWER_POLICY_ID_NO_USER_INTERACTION,
NO_USER_INTERACTION_ENABLED_COMPONENTS.clone(),
NO_USER_INTERACTION_DISABLED_COMPONENTS.clone()));
mPreemptivePowerPolicies.put(POWER_POLICY_ID_SUSPEND_TO_RAM, POWER_POLICY_SUSPEND_TO_RAM);
}
private void readPowerPolicyConfiguration() {
try (InputStream inputStream = new FileInputStream(VENDOR_POLICY_PATH)) {
readPowerPolicyFromXml(inputStream);
} catch (IOException | XmlPullParserException | PolicyXmlException e) {
Slogf.w(TAG, "Proceed without registered policies: failed to parse %s: %s",
VENDOR_POLICY_PATH, e);
}
}
@VisibleForTesting
void readPowerPolicyFromXml(InputStream stream) throws PolicyXmlException,
XmlPullParserException, IOException {
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, NAMESPACE != null);
parser.setInput(stream, null);
// Ensure is the root
parser.nextTag();
parser.require(START_TAG, NAMESPACE, TAG_POWER_POLICY);
// Check version
String version = parser.getAttributeValue(NAMESPACE, ATTR_VERSION);
if (!VALID_VERSIONS.contains(version)) {
throw new PolicyXmlException("invalid XML version: " + version);
}
ArrayMap registeredPolicies = new ArrayMap<>();
ArrayMap> policyGroups = new ArrayMap<>();
CarPowerPolicy systemPolicyOverride = null;
int type;
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
switch (parser.getName()) {
case TAG_POLICIES:
registeredPolicies = parsePolicies(parser, true);
break;
case TAG_POLICY_GROUPS:
policyGroups = parsePolicyGroups(parser);
break;
case TAG_SYSTEM_POLICY_OVERRIDES:
systemPolicyOverride = parseSystemPolicyOverrides(parser);
break;
default:
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POWER_POLICY);
}
}
validatePolicyGroups(policyGroups, registeredPolicies);
mRegisteredPowerPolicies = registeredPolicies;
mPolicyGroups = policyGroups;
reconstructSystemPowerPolicy(systemPolicyOverride);
}
private ArrayMap parsePolicies(XmlPullParser parser,
boolean includeOtherComponents) throws PolicyXmlException, XmlPullParserException,
IOException {
ArrayMap policies = new ArrayMap<>();
int type;
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
if (TAG_POLICY.equals(parser.getName())) {
String policyId = parser.getAttributeValue(NAMESPACE, ATTR_ID);
if (policyId == null || policyId.equals("")) {
throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |" + TAG_POLICY
+ "| tag");
}
if (includeOtherComponents && isSystemPowerPolicy(policyId)) {
throw new PolicyXmlException("Policy ID should not start with "
+ SYSTEM_POWER_POLICY_PREFIX);
}
policies.put(policyId, parsePolicy(parser, policyId, includeOtherComponents));
} else {
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POLICIES);
}
}
return policies;
}
private ArrayMap> parsePolicyGroups(XmlPullParser parser) throws
PolicyXmlException, XmlPullParserException, IOException {
ArrayMap> policyGroups = new ArrayMap<>();
int type;
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
if (TAG_POLICY_GROUP.equals(parser.getName())) {
String groupId = parser.getAttributeValue(NAMESPACE, ATTR_ID);
if (groupId == null || groupId.equals("")) {
throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |"
+ TAG_POLICY_GROUP + "| tag");
}
policyGroups.put(groupId, parsePolicyGroup(parser));
} else {
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POLICY_GROUPS);
}
}
return policyGroups;
}
@Nullable
private CarPowerPolicy parseSystemPolicyOverrides(XmlPullParser parser) throws
PolicyXmlException, XmlPullParserException, IOException {
ArrayMap systemOverrides = parsePolicies(parser, false);
int numOverrides = systemOverrides.size();
if (numOverrides == 0) {
return null;
}
if (numOverrides > 1) {
throw new PolicyXmlException("only one system power policy is supported: "
+ numOverrides + " system policies exist");
}
CarPowerPolicy policyOverride =
systemOverrides.get(POWER_POLICY_ID_NO_USER_INTERACTION);
if (policyOverride == null) {
throw new PolicyXmlException("system power policy id should be "
+ POWER_POLICY_ID_NO_USER_INTERACTION);
}
Set visited = new ArraySet<>();
checkSystemPowerPolicyComponents(policyOverride.getEnabledComponents(), visited);
checkSystemPowerPolicyComponents(policyOverride.getDisabledComponents(), visited);
return policyOverride;
}
private CarPowerPolicy parsePolicy(XmlPullParser parser, String policyId,
boolean includeOtherComponents) throws PolicyXmlException, XmlPullParserException,
IOException {
SparseBooleanArray components = new SparseBooleanArray();
String behavior = POWER_ONOFF_UNTOUCHED;
boolean otherComponentsProcessed = false;
int type;
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
if (TAG_COMPONENT.equals(parser.getName())) {
String id = parser.getAttributeValue(NAMESPACE, ATTR_ID);
int powerComponent = toPowerComponent(id, true);
if (powerComponent == INVALID_POWER_COMPONENT) {
throw new PolicyXmlException("invalid value(" + id + ") in |" + ATTR_ID
+ "| attribute of |" + TAG_COMPONENT + "| tag");
}
if (components.indexOfKey(powerComponent) >= 0) {
throw new PolicyXmlException(id + " is specified more than once in |"
+ TAG_COMPONENT + "| tag");
}
String state = getText(parser);
switch (state) {
case POWER_ONOFF_ON:
components.put(powerComponent, true);
break;
case POWER_ONOFF_OFF:
components.put(powerComponent, false);
break;
default:
throw new PolicyXmlException("target state(" + state + ") for " + id
+ " is not valid");
}
skip(parser);
} else if (TAG_OTHER_COMPONENTS.equals(parser.getName())) {
if (!includeOtherComponents) {
throw new PolicyXmlException("|" + TAG_OTHER_COMPONENTS
+ "| tag is not expected");
}
if (otherComponentsProcessed) {
throw new PolicyXmlException("more than one |" + TAG_OTHER_COMPONENTS
+ "| tag");
}
otherComponentsProcessed = true;
behavior = parser.getAttributeValue(NAMESPACE, ATTR_BEHAVIOR);
if (behavior == null) {
throw new PolicyXmlException("no |" + ATTR_BEHAVIOR + "| attribute of |"
+ TAG_OTHER_COMPONENTS + "| tag");
}
switch (behavior) {
case POWER_ONOFF_ON:
case POWER_ONOFF_OFF:
case POWER_ONOFF_UNTOUCHED:
break;
default:
throw new PolicyXmlException("invalid value(" + behavior + ") in |"
+ ATTR_BEHAVIOR + "| attribute of |" + TAG_OTHER_COMPONENTS
+ "| tag");
}
skip(parser);
} else {
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POLICY);
}
}
boolean enabled = false;
boolean untouched = false;
if (POWER_ONOFF_ON.equals(behavior)) {
enabled = true;
} else if (POWER_ONOFF_OFF.equals(behavior)) {
enabled = false;
} else {
untouched = true;
}
if (!untouched) {
for (int component = FIRST_POWER_COMPONENT;
component <= LAST_POWER_COMPONENT; component++) {
if (components.indexOfKey(component) >= 0) continue;
components.put(component, enabled);
}
}
return new CarPowerPolicy(policyId, toIntArray(components, true),
toIntArray(components, false));
}
private SparseArray parsePolicyGroup(XmlPullParser parser) throws PolicyXmlException,
XmlPullParserException, IOException {
SparseArray policyGroup = new SparseArray<>();
int type;
Set visited = new ArraySet<>();
while ((type = parser.next()) != END_DOCUMENT && type != END_TAG) {
if (type != START_TAG) continue;
if (TAG_DEFAULT_POLICY.equals(parser.getName())) {
String id = parser.getAttributeValue(NAMESPACE, ATTR_ID);
if (id == null || id.isEmpty()) {
throw new PolicyXmlException("no |" + ATTR_ID + "| attribute of |"
+ TAG_DEFAULT_POLICY + "| tag");
}
String state = parser.getAttributeValue(NAMESPACE, ATTR_STATE);
int powerState = toPowerState(state);
if (powerState == INVALID_POWER_STATE) {
throw new PolicyXmlException("invalid value(" + state + ") in |" + ATTR_STATE
+ "| attribute of |" + TAG_DEFAULT_POLICY + "| tag");
}
if (visited.contains(powerState)) {
throw new PolicyXmlException("power state(" + state
+ ") is specified more than once");
}
policyGroup.put(powerState, id);
visited.add(powerState);
skip(parser);
} else if (TAG_NO_DEFAULT_POLICY.equals(parser.getName())) {
String state = parser.getAttributeValue(NAMESPACE, ATTR_STATE);
int powerState = toPowerState(state);
if (powerState == INVALID_POWER_STATE) {
throw new PolicyXmlException("invalid value(" + state + ") in |" + ATTR_STATE
+ "| attribute of |" + TAG_DEFAULT_POLICY + "| tag");
}
if (visited.contains(powerState)) {
throw new PolicyXmlException("power state(" + state
+ ") is specified more than once");
}
visited.add(powerState);
skip(parser);
} else {
throw new PolicyXmlException("unknown tag: " + parser.getName() + " under "
+ TAG_POLICY_GROUP);
}
}
return policyGroup;
}
private void validatePolicyGroups(ArrayMap> policyGroups,
ArrayMap registeredPolicies) throws PolicyXmlException {
for (Map.Entry> entry : policyGroups.entrySet()) {
SparseArray group = entry.getValue();
for (int i = 0; i < group.size(); i++) {
String policyId = group.valueAt(i);
if (!registeredPolicies.containsKey(group.valueAt(i))) {
throw new PolicyXmlException("group(id: " + entry.getKey()
+ ") contains invalid policy(id: " + policyId + ")");
}
}
}
}
private void reconstructSystemPowerPolicy(@Nullable CarPowerPolicy policyOverride) {
if (policyOverride == null) return;
List enabledComponents = Arrays.stream(NO_USER_INTERACTION_ENABLED_COMPONENTS)
.boxed().collect(Collectors.toList());
List disabledComponents = Arrays.stream(NO_USER_INTERACTION_DISABLED_COMPONENTS)
.boxed().collect(Collectors.toList());
int[] overrideEnabledComponents = policyOverride.getEnabledComponents();
int[] overrideDisabledComponents = policyOverride.getDisabledComponents();
for (int i = 0; i < overrideEnabledComponents.length; i++) {
removeComponent(disabledComponents, overrideEnabledComponents[i]);
addComponent(enabledComponents, overrideEnabledComponents[i]);
}
for (int i = 0; i < overrideDisabledComponents.length; i++) {
removeComponent(enabledComponents, overrideDisabledComponents[i]);
addComponent(disabledComponents, overrideDisabledComponents[i]);
}
mPreemptivePowerPolicies.put(POWER_POLICY_ID_NO_USER_INTERACTION,
new CarPowerPolicy(POWER_POLICY_ID_NO_USER_INTERACTION,
CarServiceUtils.toIntArray(enabledComponents),
CarServiceUtils.toIntArray(disabledComponents)));
}
private void removeComponent(List components, int component) {
int index = components.lastIndexOf(component);
if (index != -1) {
components.remove(index);
}
}
private void addComponent(List components, int component) {
int index = components.lastIndexOf(component);
if (index == -1) {
components.add(component);
}
}
private String getText(XmlPullParser parser) throws PolicyXmlException, XmlPullParserException,
IOException {
if (parser.getEventType() != START_TAG) {
throw new PolicyXmlException("tag pair doesn't match");
}
parser.next();
if (parser.getEventType() != TEXT) {
throw new PolicyXmlException("tag value is not found");
}
return parser.getText();
}
private void skip(XmlPullParser parser) throws PolicyXmlException, XmlPullParserException,
IOException {
int type = parser.getEventType();
if (type != START_TAG && type != TEXT) {
throw new PolicyXmlException("tag pair doesn't match");
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case END_TAG:
depth--;
break;
case START_TAG:
depth++;
break;
}
}
}
void checkSystemPowerPolicyComponents(int[] components, Set visited) throws
PolicyXmlException {
for (int i = 0; i < components.length; i++) {
int component = components[i];
if (!isOverridableComponent(component)) {
throw new PolicyXmlException("Power component(" + powerComponentToString(component)
+ ") cannot be overridden");
}
if (visited.contains(component)) {
throw new PolicyXmlException("Power component(" + powerComponentToString(component)
+ ") is specified more than once");
}
visited.add(component);
}
}
boolean isOverridableComponent(int component) {
return SYSTEM_POLICY_CONFIGURABLE_COMPONENTS.contains(component);
}
private String toString(CarPowerPolicy policy) {
return policy.getPolicyId() + "(enabledComponents: "
+ componentsToString(policy.getEnabledComponents()) + " | disabledComponents: "
+ componentsToString(policy.getDisabledComponents()) + ")";
}
private String componentsToString(int[] components) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < components.length; i++) {
if (i > 0) buffer.append(", ");
buffer.append(powerComponentToString(components[i]));
}
return buffer.toString();
}
@PolicyOperationStatus.ErrorCode
int parseComponents(String[] componentArr, boolean enabled, SparseBooleanArray components) {
for (int i = 0; i < componentArr.length; i++) {
int component = toPowerComponent(componentArr[i], false);
if (component == INVALID_POWER_COMPONENT) {
int error = PolicyOperationStatus.ERROR_INVALID_POWER_COMPONENT;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, componentArr[i]));
return error;
}
if (components.indexOfKey(component) >= 0) {
int error = PolicyOperationStatus.ERROR_DUPLICATED_POWER_COMPONENT;
Slogf.w(TAG, PolicyOperationStatus.errorCodeToString(error, componentArr[i]));
return error;
}
components.put(component, enabled);
}
return PolicyOperationStatus.OK;
}
static int toPowerState(String state) {
if (state == null) {
return INVALID_POWER_STATE;
}
switch (state) {
case POWER_STATE_WAIT_FOR_VHAL:
return VehicleApPowerStateReport.WAIT_FOR_VHAL;
case POWER_STATE_ON:
return VehicleApPowerStateReport.ON;
default:
return INVALID_POWER_STATE;
}
}
static String powerStateToString(int state) {
switch (state) {
case VehicleApPowerStateReport.WAIT_FOR_VHAL:
return POWER_STATE_WAIT_FOR_VHAL;
case VehicleApPowerStateReport.ON:
return POWER_STATE_ON;
default:
return "unknown power state";
}
}
static boolean isSystemPowerPolicy(String policyId) {
return policyId == null ? false : policyId.startsWith(SYSTEM_POWER_POLICY_PREFIX);
}
private static int[] toIntArray(SparseBooleanArray array, boolean value) {
int arraySize = array.size();
int returnSize = 0;
for (int i = 0; i < arraySize; i++) {
if (array.valueAt(i) == value) returnSize++;
}
int[] ret = new int[returnSize];
int count = 0;
for (int i = 0; i < arraySize; i++) {
if (array.valueAt(i) == value) {
ret[count++] = array.keyAt(i);
}
}
return ret;
}
private static boolean containsComponent(int[] arr, int component) {
for (int element : arr) {
if (element == component) return true;
}
return false;
}
@VisibleForTesting
static final class PolicyXmlException extends Exception {
PolicyXmlException(String message) {
super(message);
}
}
}