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