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