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