1 /*
2  * Copyright (C) 2021 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 android.car.cts.powerpolicy;
18 
19 import com.android.tradefed.log.LogUtil.CLog;
20 
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.Objects;
26 
27 public final class CpmsFrameworkLayerStateInfo {
28     private static final int STRING_BUILDER_BUF_SIZE = 1024;
29 
30     public static final String COMMAND = "dumpsys car_service --services"
31             + " CarPowerManagementService";
32     public static final String CURRENT_STATE_HDR = "mCurrentState:";
33     public static final String CURRENT_POLICY_ID_HDR = "mCurrentPowerPolicyId:";
34     public static final String PENDING_POLICY_ID_HDR = "mPendingPowerPolicyId:";
35     public static final String CURRENT_POLICY_GROUP_ID_HDR = "mCurrentPowerPolicyGroupId:";
36     public static final String NUMBER_POLICY_LISTENERS_HDR = "# of power policy change listener:";
37     public static final String POWER_POLICY_GROUPS_HDR = "Power policy groups:";
38     public static final String PREEMPTIVE_POWER_POLICY_HDR = "Preemptive power policy:";
39     public static final String COMPONENT_STATE_HDR = "Power components state:";
40     public static final String COMPONENT_CONTROLLED_HDR =
41             "Components powered off by power policy:";
42     public static final String COMPONENT_CHANGED_HDR = "Components changed by the last policy:";
43     public static final String MONITORING_HW_HDR = "Monitoring HW state signal:";
44     public static final String SILENT_MODE_BY_HW_HDR = "Silent mode by HW state signal:";
45     public static final String FORCED_SILENT_MODE_HDR = "Forced silent mode:";
46 
47     private static final String[] COMPONENT_LIST = {"AUDIO", "MEDIA", "DISPLAY", "BLUETOOTH",
48             "WIFI", "CELLULAR", "ETHERNET", "PROJECTION", "NFC", "INPUT", "VOICE_INTERACTION",
49             "VISUAL_INTERACTION", "TRUSTED_DEVICE_DETECTION", "LOCATION", "MICROPHONE", "CPU"};
50     private static final HashSet COMPONENT_SET = new HashSet(Arrays.asList(COMPONENT_LIST));
51 
52     private final ArrayList<String> mEnables;
53     private final ArrayList<String> mDisables;
54     private final ArrayList<String> mControlledEnables;
55     private final ArrayList<String> mControlledDisables;
56     private final String[] mChangedComponents;
57     private final PowerPolicyGroups mPowerPolicyGroups;
58     private final String mCurrentPolicyId;
59     private final String mPendingPolicyId;
60     private final String mCurrentPolicyGroupId;
61     private final int mNumberPolicyListeners;
62     private final boolean mMonitoringHw;
63     private final boolean mSilentModeByHw;
64     private final boolean mForcedSilentMode;
65     private final int mCurrentState;
66 
CpmsFrameworkLayerStateInfo(String currentPolicyId, String pendingPolicyId, String currentPolicyGroupId, int numberPolicyListeners, String[] changedComponents, ArrayList<String> enables, ArrayList<String> disables, PowerPolicyGroups policyGroups, ArrayList<String> controlledEnables, ArrayList<String> controlledDisables, boolean monitoringHw, boolean silentModeByHw, boolean forcedSilentMode, int currentState)67     private CpmsFrameworkLayerStateInfo(String currentPolicyId, String pendingPolicyId,
68             String currentPolicyGroupId, int numberPolicyListeners, String[] changedComponents,
69             ArrayList<String> enables, ArrayList<String> disables, PowerPolicyGroups policyGroups,
70             ArrayList<String> controlledEnables, ArrayList<String> controlledDisables,
71             boolean monitoringHw, boolean silentModeByHw, boolean forcedSilentMode,
72             int currentState) {
73         mEnables = enables;
74         mDisables = disables;
75         mControlledEnables = controlledEnables;
76         mControlledDisables = controlledDisables;
77         mChangedComponents = changedComponents;
78         mPowerPolicyGroups = policyGroups;
79         mCurrentPolicyId = currentPolicyId;
80         mPendingPolicyId = pendingPolicyId;
81         mCurrentPolicyGroupId = currentPolicyGroupId;
82         mNumberPolicyListeners = numberPolicyListeners;
83         mMonitoringHw = monitoringHw;
84         mSilentModeByHw = silentModeByHw;
85         mForcedSilentMode = forcedSilentMode;
86         mCurrentState = currentState;
87     }
88 
getCurrentPolicyId()89     public String getCurrentPolicyId() {
90         return mCurrentPolicyId;
91     }
92 
getPendingPolicyId()93     public String getPendingPolicyId() {
94         return mPendingPolicyId;
95     }
96 
getCurrentState()97     public int getCurrentState() {
98         return mCurrentState;
99     }
100 
getForcedSilentMode()101     public boolean getForcedSilentMode() {
102         return mForcedSilentMode;
103     }
104 
getCurrentEnabledComponents()105     public PowerPolicyDef.PowerComponent[] getCurrentEnabledComponents() {
106         return PowerPolicyDef.PowerComponent.asComponentArray(mEnables);
107     }
108 
getCurrentDisabledComponents()109     public PowerPolicyDef.PowerComponent[] getCurrentDisabledComponents() {
110         return PowerPolicyDef.PowerComponent.asComponentArray(mDisables);
111     }
112 
getCurrentPolicyGroupId()113     public String getCurrentPolicyGroupId() {
114         return mCurrentPolicyGroupId;
115     }
116 
getPowerPolicyGroups()117     public PowerPolicyGroups getPowerPolicyGroups() {
118         return mPowerPolicyGroups;
119     }
120 
getNumberPolicyListeners()121     public int getNumberPolicyListeners() {
122         return mNumberPolicyListeners;
123     }
124 
isComponentOn(String component)125     public boolean isComponentOn(String component) {
126         return mEnables.contains(component);
127     }
128 
isComponentOff(String component)129     public boolean isComponentOff(String component) {
130         return mDisables.contains(component);
131     }
132 
133     @Override
toString()134     public String toString() {
135         StringBuilder sb = new StringBuilder(STRING_BUILDER_BUF_SIZE);
136         sb.append("mCurrentState=").append(mCurrentState).append(' ');
137         sb.append("mCurrentPolicyId=").append(mCurrentPolicyId).append(' ');
138         sb.append("mPendingPolicyId=").append(mPendingPolicyId).append(' ');
139         sb.append("mCurrentPolicyGroupId=").append(mCurrentPolicyGroupId).append(' ');
140         sb.append("mNumberPolicyListeners=").append(mNumberPolicyListeners).append(' ');
141         sb.append("silentmode=").append(mMonitoringHw).append(',');
142         sb.append(mSilentModeByHw).append(',').append(mForcedSilentMode).append(' ');
143         sb.append("enables=").append(String.join(",", mEnables)).append(' ');
144         sb.append("disables=").append(String.join(",", mDisables));
145         sb.append("controlledEnables=").append(String.join(",", mControlledEnables)).append(' ');
146         sb.append("controlledDisables=").append(String.join(",", mControlledDisables));
147         sb.append("changedComponents=").append(String.join(",", mChangedComponents));
148         return sb.toString();
149     }
150 
151     @Override
equals(Object o)152     public boolean equals(Object o) {
153         if (this == o) return true;
154         if (o == null || getClass() != o.getClass()) return false;
155         CpmsFrameworkLayerStateInfo that = (CpmsFrameworkLayerStateInfo) o;
156         return mCurrentState == that.mCurrentState
157                 && mMonitoringHw == that.mMonitoringHw
158                 && mSilentModeByHw == that.mSilentModeByHw
159                 && mForcedSilentMode == that.mForcedSilentMode
160                 && mNumberPolicyListeners == that.mNumberPolicyListeners
161                 && mEnables.equals(that.mEnables)
162                 && mDisables.equals(that.mDisables)
163                 && mPowerPolicyGroups.equals(that.mPowerPolicyGroups)
164                 && mControlledEnables.equals(that.mControlledEnables)
165                 && mControlledDisables.equals(that.mControlledDisables)
166                 && Arrays.equals(mChangedComponents, that.mChangedComponents)
167                 && Objects.equals(mCurrentPolicyId, that.mCurrentPolicyId)
168                 && Objects.equals(mPendingPolicyId, that.mPendingPolicyId)
169                 && Objects.equals(mCurrentPolicyGroupId, that.mCurrentPolicyGroupId);
170     }
171 
172     @Override
hashCode()173     public int hashCode() {
174         return Objects.hash(mEnables, mDisables, mControlledEnables, mControlledDisables,
175                 mChangedComponents, mPowerPolicyGroups, mCurrentPolicyId, mPendingPolicyId,
176                 mCurrentPolicyGroupId, mCurrentState, mMonitoringHw, mSilentModeByHw,
177                 mForcedSilentMode, mNumberPolicyListeners);
178     }
179 
parse(String cmdOutput)180     public static CpmsFrameworkLayerStateInfo parse(String cmdOutput) throws Exception {
181         int currentState = -1;
182         String currentPolicyId = null;
183         String pendingPolicyId = null;
184         String currentPolicyGroupId = null;
185         ArrayList<String> enables = null;
186         ArrayList<String> disables = null;
187         ArrayList<String> controlledEnables = null;
188         ArrayList<String> controlledDisables = null;
189         String[] changedComponents = null;
190         PowerPolicyGroups policyGroups = null;
191         boolean monitoringHw = false;
192         boolean silentModeByHw = false;
193         boolean forcedSilentMode = false;
194         int numberPolicyListeners = 0;
195 
196         String[] lines = cmdOutput.split("\n");
197         StateInfoParser parser = new StateInfoParser(lines);
198         HashSet<String> headerCounter = new HashSet<String>();
199         String header;
200         while ((header = parser.searchHeader()) != null) {
201             switch (header) {
202                 case CURRENT_STATE_HDR:
203                     currentState = parser.getIntData(CURRENT_STATE_HDR);
204                     break;
205                 case CURRENT_POLICY_ID_HDR:
206                     currentPolicyId = parser.getStringData(CURRENT_POLICY_ID_HDR);
207                     break;
208                 case PENDING_POLICY_ID_HDR:
209                     pendingPolicyId = parser.getStringData(PENDING_POLICY_ID_HDR);
210                     break;
211                 case CURRENT_POLICY_GROUP_ID_HDR:
212                     currentPolicyGroupId = parser.getStringData(CURRENT_POLICY_GROUP_ID_HDR);
213                     break;
214                 case POWER_POLICY_GROUPS_HDR:
215                     ArrayList<String> groupList = parser.getStringArray(POWER_POLICY_GROUPS_HDR,
216                             PREEMPTIVE_POWER_POLICY_HDR);
217                     policyGroups = PowerPolicyGroups.parse(groupList);
218                     break;
219                 case COMPONENT_STATE_HDR:
220                     parser.parseComponentStates(COMPONENT_STATE_HDR,
221                             COMPONENT_CONTROLLED_HDR, true);
222                     enables = parser.getEnables();
223                     disables = parser.getDisables();
224                     Collections.sort(enables);
225                     Collections.sort(disables);
226                     break;
227                 case COMPONENT_CONTROLLED_HDR:
228                     parser.parseComponentStates(COMPONENT_CONTROLLED_HDR,
229                             COMPONENT_CHANGED_HDR, false);
230                     controlledEnables = parser.getEnables();
231                     controlledDisables = parser.getDisables();
232                     Collections.sort(controlledEnables);
233                     Collections.sort(controlledDisables);
234                     break;
235                 case COMPONENT_CHANGED_HDR:
236                     changedComponents = parser.getChangedComponents(COMPONENT_CHANGED_HDR,
237                             MONITORING_HW_HDR);
238                     break;
239                 case MONITORING_HW_HDR:
240                     monitoringHw = parser.getBooleanData(MONITORING_HW_HDR);
241                     break;
242                 case SILENT_MODE_BY_HW_HDR:
243                     silentModeByHw = parser.getBooleanData(SILENT_MODE_BY_HW_HDR);
244                     break;
245                 case FORCED_SILENT_MODE_HDR:
246                     forcedSilentMode = parser.getBooleanData(FORCED_SILENT_MODE_HDR);
247                     break;
248                 case NUMBER_POLICY_LISTENERS_HDR:
249                     numberPolicyListeners = parser.getIntData(NUMBER_POLICY_LISTENERS_HDR);
250                     break;
251                 default:
252                     throw new IllegalArgumentException("parser header mismatch: " + header);
253             }
254             headerCounter.add(header);
255         }
256 
257         if (headerCounter.size() != StateInfoParser.HEADERS.length) {
258             String errMsg = "miss headers. got: " + headerCounter + " expected: "
259                     + String.join(",", StateInfoParser.HEADERS);
260             throw new IllegalArgumentException(errMsg);
261         }
262 
263         return new CpmsFrameworkLayerStateInfo(currentPolicyId, pendingPolicyId,
264                 currentPolicyGroupId, numberPolicyListeners, changedComponents, enables,
265                 disables, policyGroups, controlledEnables, controlledDisables, monitoringHw,
266                 silentModeByHw, forcedSilentMode, currentState);
267     }
268 
269     private static final class StateInfoParser {
270         private static final String[] HEADERS = {
271             CURRENT_STATE_HDR,
272             CURRENT_POLICY_ID_HDR,
273             PENDING_POLICY_ID_HDR,
274             CURRENT_POLICY_GROUP_ID_HDR,
275             NUMBER_POLICY_LISTENERS_HDR,
276             COMPONENT_STATE_HDR,
277             COMPONENT_CONTROLLED_HDR,
278             POWER_POLICY_GROUPS_HDR,
279             COMPONENT_CHANGED_HDR,
280             MONITORING_HW_HDR,
281             SILENT_MODE_BY_HW_HDR,
282             FORCED_SILENT_MODE_HDR
283         };
284         private final String[] mLines;
285         private ArrayList<String> mEnables;
286         private ArrayList<String> mDisables;
287         private int mIdx = 0;
288 
StateInfoParser(String[] lines)289         private StateInfoParser(String[] lines) {
290             mLines = lines;
291         }
292 
getEnables()293         private ArrayList<String> getEnables() {
294             return mEnables;
295         }
296 
getDisables()297         private ArrayList<String> getDisables() {
298             return mDisables;
299         }
300 
getIntData(String header)301         private int getIntData(String header) throws Exception {
302             int val = 0;
303             switch (header) {
304                 case CURRENT_STATE_HDR:
305                     String[] tokens = mLines[mIdx].split(",*\\s");
306                     if (tokens.length != 6) {
307                         throw new IllegalArgumentException("malformatted mCurrentState: "
308                                 + mLines[mIdx]);
309                     }
310                     val = Integer.parseInt(tokens[4].trim().substring(tokens[4].length() - 1));
311                     break;
312                 case NUMBER_POLICY_LISTENERS_HDR:
313                     int strLen = mLines[mIdx].length();
314                     val = Integer.parseInt(mLines[mIdx].substring(strLen - 1).trim());
315                     break;
316                 default:
317                     break;
318             }
319             return val;
320         }
321 
getStringData(String header)322         private String getStringData(String header) {
323             String val = null;
324             if (mLines[mIdx].trim().length() != header.length()) {
325                 val = mLines[mIdx].trim().substring(header.length()).trim();
326             }
327             return val;
328         }
329 
getStringArray(String startHdr, String endHdr)330         private ArrayList<String> getStringArray(String startHdr, String endHdr)
331                 throws Exception {
332             if (!mLines[mIdx].contains(startHdr)) {
333                 String errMsg = String.format("expected start header %s at line %d : %s",
334                         startHdr, mIdx, mLines[mIdx]);
335                 throw new IllegalArgumentException(errMsg);
336             }
337 
338             ArrayList<String> strArray = new ArrayList<String>();
339             while (++mIdx < mLines.length && !mLines[mIdx].contains(endHdr)) {
340                 strArray.add(mLines[mIdx]);
341             }
342             mIdx--;
343 
344             if (mIdx == (mLines.length - 1)) {
345                 throw new IllegalArgumentException("reaches the end while get " + startHdr);
346             }
347             return strArray;
348         }
349 
parseComponentStates(String startHdr, String endHdr, boolean hasStateInfo)350         private void parseComponentStates(String startHdr, String endHdr,
351                 boolean hasStateInfo) throws Exception {
352             mEnables = new ArrayList<String>();
353             mDisables = new ArrayList<String>();
354             while (mIdx < (mLines.length - 1) && !mLines[++mIdx].contains(endHdr)) {
355                 String stateStr = mLines[mIdx].trim();
356                 String[] vals = stateStr.split(":\\s");
357                 if (hasStateInfo && vals.length != 2) {
358                     String errMsg = String.format("wrong format at %d in: %s ", mIdx, stateStr);
359                     CLog.e(errMsg);
360                     throw new IllegalArgumentException(errMsg);
361                 }
362 
363                 for (int i = 0; i < vals.length; i++) {
364                     vals[i] = vals[i].trim();
365                 }
366 
367                 if (!COMPONENT_SET.contains(vals[0])) {
368                     String errMsg = String.format("invalid component at %d with %s in: %s",
369                             mIdx, vals[0], stateStr);
370                     CLog.e(errMsg);
371                     throw new IllegalArgumentException(errMsg);
372                 }
373 
374                 if (hasStateInfo) {
375                     if (vals[1].startsWith("on")) {
376                         mEnables.add(vals[0]);
377                     } else if (vals[1].startsWith("off")) {
378                         mDisables.add(vals[0]);
379                     } else {
380                         String errMsg =
381                                 String.format("wrong state value at %d with (%s, %s) in: %s",
382                                 mIdx, vals[0], vals[1], stateStr);
383                         CLog.e(errMsg);
384                         throw new IllegalArgumentException(errMsg);
385                     }
386                 } else {
387                     mDisables.add(vals[0]);
388                 }
389             }
390             mIdx--;
391 
392             if (mIdx == (mLines.length - 1)) {
393                 throw new IllegalArgumentException("reaches the end while parse " + startHdr);
394             }
395         }
396 
getChangedComponents(String startHdr, String endHdr)397         private String[] getChangedComponents(String startHdr, String endHdr) {
398             int idx = mLines[mIdx].indexOf(endHdr);
399             String compStr;
400             if (idx < 0) {
401                 compStr = mLines[mIdx].substring(startHdr.length());
402             } else {
403                 compStr = mLines[mIdx].substring(startHdr.length(), idx);
404                 mLines[mIdx] = mLines[mIdx].substring(idx);
405                 mIdx--;
406             }
407             return compStr.split(",\\s*");
408         }
409 
getBooleanData(String header)410         private boolean getBooleanData(String header) {
411             return Boolean.parseBoolean(mLines[mIdx].trim().substring(header.length()).trim());
412         }
413 
searchHeader()414         private String searchHeader() {
415             String header = null;
416             for (mIdx++; mIdx < mLines.length; mIdx++) {
417                 if (mLines[mIdx].trim().isEmpty()) {
418                     continue;
419                 }
420 
421                 int firstHdrPos = mLines[mIdx].length() + 1;
422                 for (int i = 0; i < HEADERS.length; i++) {
423                     int tempHdrPos = mLines[mIdx].indexOf(HEADERS[i]);
424                     if (tempHdrPos >= 0 && (firstHdrPos > tempHdrPos)) {
425                         firstHdrPos = tempHdrPos;
426                         header = HEADERS[i];
427                     }
428                 }
429                 if (header != null) {
430                     break;
431                 }
432             }
433 
434             return header;
435         }
436     }
437 }
438