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