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