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