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