blob: 08abe56b83903ffca961da960e1d58d88087ae8a [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.car.cts.powerpolicy;
import com.android.tradefed.log.LogUtil.CLog;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class CpmsFrameworkLayerStateInfo {
private static final int STRING_BUILDER_BUF_SIZE = 1024;
public static final String COMMAND = "dumpsys car_service --services"
+ " CarPowerManagementService";
public static final String CURRENT_STATE_HDR = "mCurrentState:";
public static final String CURRENT_POLICY_ID_HDR = "mCurrentPowerPolicyId:";
public static final String PENDING_POLICY_ID_HDR = "mPendingPowerPolicyId:";
public static final String CURRENT_POLICY_GROUP_ID_HDR = "mCurrentPowerPolicyGroupId:";
public static final String NUMBER_POLICY_LISTENERS_HDR = "# of power policy change listener:";
public static final String POWER_POLICY_GROUPS_HDR = "Power policy groups:";
public static final String PREEMPTIVE_POWER_POLICY_HDR = "Preemptive power policy:";
public static final String COMPONENT_STATE_HDR = "Power components state:";
public static final String COMPONENT_CONTROLLED_HDR =
"Components powered off by power policy:";
public static final String COMPONENT_CHANGED_HDR = "Components changed by the last policy:";
public static final String MONITORING_HW_HDR = "Monitoring HW state signal:";
public static final String SILENT_MODE_BY_HW_HDR = "Silent mode by HW state signal:";
public static final String FORCED_SILENT_MODE_HDR = "Forced silent mode:";
private static final String[] COMPONENT_LIST = {"AUDIO", "MEDIA", "DISPLAY", "BLUETOOTH",
"WIFI", "CELLULAR", "ETHERNET", "PROJECTION", "NFC", "INPUT", "VOICE_INTERACTION",
"VISUAL_INTERACTION", "TRUSTED_DEVICE_DETECTION", "LOCATION", "MICROPHONE", "CPU"};
private static final HashSet COMPONENT_SET = new HashSet(Arrays.asList(COMPONENT_LIST));
private final ArrayList<String> mEnables;
private final ArrayList<String> mDisables;
private final ArrayList<String> mControlledEnables;
private final ArrayList<String> mControlledDisables;
private final String[] mChangedComponents;
private final PowerPolicyGroups mPowerPolicyGroups;
private final String mCurrentPolicyId;
private final String mPendingPolicyId;
private final String mCurrentPolicyGroupId;
private final int mNumberPolicyListeners;
private final boolean mMonitoringHw;
private final boolean mSilentModeByHw;
private final boolean mForcedSilentMode;
private final int mCurrentState;
private 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) {
mEnables = enables;
mDisables = disables;
mControlledEnables = controlledEnables;
mControlledDisables = controlledDisables;
mChangedComponents = changedComponents;
mPowerPolicyGroups = policyGroups;
mCurrentPolicyId = currentPolicyId;
mPendingPolicyId = pendingPolicyId;
mCurrentPolicyGroupId = currentPolicyGroupId;
mNumberPolicyListeners = numberPolicyListeners;
mMonitoringHw = monitoringHw;
mSilentModeByHw = silentModeByHw;
mForcedSilentMode = forcedSilentMode;
mCurrentState = currentState;
}
public String getCurrentPolicyId() {
return mCurrentPolicyId;
}
public String getPendingPolicyId() {
return mPendingPolicyId;
}
public int getCurrentState() {
return mCurrentState;
}
public boolean getForcedSilentMode() {
return mForcedSilentMode;
}
public PowerPolicyDef.PowerComponent[] getCurrentEnabledComponents() {
return PowerPolicyDef.PowerComponent.asComponentArray(mEnables);
}
public PowerPolicyDef.PowerComponent[] getCurrentDisabledComponents() {
return PowerPolicyDef.PowerComponent.asComponentArray(mDisables);
}
public String getCurrentPolicyGroupId() {
return mCurrentPolicyGroupId;
}
public PowerPolicyGroups getPowerPolicyGroups() {
return mPowerPolicyGroups;
}
public int getNumberPolicyListeners() {
return mNumberPolicyListeners;
}
public boolean isComponentOn(String component) {
return mEnables.contains(component);
}
public boolean isComponentOff(String component) {
return mDisables.contains(component);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(STRING_BUILDER_BUF_SIZE);
sb.append("mCurrentState=").append(mCurrentState).append(' ');
sb.append("mCurrentPolicyId=").append(mCurrentPolicyId).append(' ');
sb.append("mPendingPolicyId=").append(mPendingPolicyId).append(' ');
sb.append("mCurrentPolicyGroupId=").append(mCurrentPolicyGroupId).append(' ');
sb.append("mNumberPolicyListeners=").append(mNumberPolicyListeners).append(' ');
sb.append("silentmode=").append(mMonitoringHw).append(',');
sb.append(mSilentModeByHw).append(',').append(mForcedSilentMode).append(' ');
sb.append("enables=").append(String.join(",", mEnables)).append(' ');
sb.append("disables=").append(String.join(",", mDisables));
sb.append("controlledEnables=").append(String.join(",", mControlledEnables)).append(' ');
sb.append("controlledDisables=").append(String.join(",", mControlledDisables));
sb.append("changedComponents=").append(String.join(",", mChangedComponents));
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CpmsFrameworkLayerStateInfo that = (CpmsFrameworkLayerStateInfo) o;
return mCurrentState == that.mCurrentState
&& mMonitoringHw == that.mMonitoringHw
&& mSilentModeByHw == that.mSilentModeByHw
&& mForcedSilentMode == that.mForcedSilentMode
&& mNumberPolicyListeners == that.mNumberPolicyListeners
&& mEnables.equals(that.mEnables)
&& mDisables.equals(that.mDisables)
&& mPowerPolicyGroups.equals(that.mPowerPolicyGroups)
&& mControlledEnables.equals(that.mControlledEnables)
&& mControlledDisables.equals(that.mControlledDisables)
&& Arrays.equals(mChangedComponents, that.mChangedComponents)
&& Objects.equals(mCurrentPolicyId, that.mCurrentPolicyId)
&& Objects.equals(mPendingPolicyId, that.mPendingPolicyId)
&& Objects.equals(mCurrentPolicyGroupId, that.mCurrentPolicyGroupId);
}
@Override
public int hashCode() {
return Objects.hash(mEnables, mDisables, mControlledEnables, mControlledDisables,
mChangedComponents, mPowerPolicyGroups, mCurrentPolicyId, mPendingPolicyId,
mCurrentPolicyGroupId, mCurrentState, mMonitoringHw, mSilentModeByHw,
mForcedSilentMode, mNumberPolicyListeners);
}
public static CpmsFrameworkLayerStateInfo parse(String cmdOutput) throws Exception {
int currentState = -1;
String currentPolicyId = null;
String pendingPolicyId = null;
String currentPolicyGroupId = null;
ArrayList<String> enables = null;
ArrayList<String> disables = null;
ArrayList<String> controlledEnables = null;
ArrayList<String> controlledDisables = null;
String[] changedComponents = null;
PowerPolicyGroups policyGroups = null;
boolean monitoringHw = false;
boolean silentModeByHw = false;
boolean forcedSilentMode = false;
int numberPolicyListeners = 0;
String[] lines = cmdOutput.split("\n");
StateInfoParser parser = new StateInfoParser(lines);
HashSet<String> headerCounter = new HashSet<String>();
String header;
while ((header = parser.searchHeader()) != null) {
switch (header) {
case CURRENT_STATE_HDR:
currentState = parser.getIntData(CURRENT_STATE_HDR);
break;
case CURRENT_POLICY_ID_HDR:
currentPolicyId = parser.getStringData(CURRENT_POLICY_ID_HDR);
break;
case PENDING_POLICY_ID_HDR:
pendingPolicyId = parser.getStringData(PENDING_POLICY_ID_HDR);
break;
case CURRENT_POLICY_GROUP_ID_HDR:
currentPolicyGroupId = parser.getStringData(CURRENT_POLICY_GROUP_ID_HDR);
break;
case POWER_POLICY_GROUPS_HDR:
ArrayList<String> groupList = parser.getStringArray(POWER_POLICY_GROUPS_HDR,
PREEMPTIVE_POWER_POLICY_HDR);
policyGroups = PowerPolicyGroups.parse(groupList);
break;
case COMPONENT_STATE_HDR:
parser.parseComponentStates(COMPONENT_STATE_HDR,
COMPONENT_CONTROLLED_HDR, true);
enables = parser.getEnables();
disables = parser.getDisables();
Collections.sort(enables);
Collections.sort(disables);
break;
case COMPONENT_CONTROLLED_HDR:
parser.parseComponentStates(COMPONENT_CONTROLLED_HDR,
COMPONENT_CHANGED_HDR, false);
controlledEnables = parser.getEnables();
controlledDisables = parser.getDisables();
Collections.sort(controlledEnables);
Collections.sort(controlledDisables);
break;
case COMPONENT_CHANGED_HDR:
changedComponents = parser.getChangedComponents(COMPONENT_CHANGED_HDR,
MONITORING_HW_HDR);
break;
case MONITORING_HW_HDR:
monitoringHw = parser.getBooleanData(MONITORING_HW_HDR);
break;
case SILENT_MODE_BY_HW_HDR:
silentModeByHw = parser.getBooleanData(SILENT_MODE_BY_HW_HDR);
break;
case FORCED_SILENT_MODE_HDR:
forcedSilentMode = parser.getBooleanData(FORCED_SILENT_MODE_HDR);
break;
case NUMBER_POLICY_LISTENERS_HDR:
numberPolicyListeners = parser.getIntData(NUMBER_POLICY_LISTENERS_HDR);
break;
default:
throw new IllegalArgumentException("parser header mismatch: " + header);
}
headerCounter.add(header);
}
if (headerCounter.size() != StateInfoParser.HEADERS.length) {
String errMsg = "miss headers. got: " + headerCounter + " expected: "
+ String.join(",", StateInfoParser.HEADERS);
throw new IllegalArgumentException(errMsg);
}
return new CpmsFrameworkLayerStateInfo(currentPolicyId, pendingPolicyId,
currentPolicyGroupId, numberPolicyListeners, changedComponents, enables,
disables, policyGroups, controlledEnables, controlledDisables, monitoringHw,
silentModeByHw, forcedSilentMode, currentState);
}
private static final class StateInfoParser {
private static final String[] HEADERS = {
CURRENT_STATE_HDR,
CURRENT_POLICY_ID_HDR,
PENDING_POLICY_ID_HDR,
CURRENT_POLICY_GROUP_ID_HDR,
NUMBER_POLICY_LISTENERS_HDR,
COMPONENT_STATE_HDR,
COMPONENT_CONTROLLED_HDR,
POWER_POLICY_GROUPS_HDR,
COMPONENT_CHANGED_HDR,
MONITORING_HW_HDR,
SILENT_MODE_BY_HW_HDR,
FORCED_SILENT_MODE_HDR
};
private final String[] mLines;
private ArrayList<String> mEnables;
private ArrayList<String> mDisables;
private int mIdx = 0;
private StateInfoParser(String[] lines) {
mLines = lines;
}
private ArrayList<String> getEnables() {
return mEnables;
}
private ArrayList<String> getDisables() {
return mDisables;
}
private int getIntData(String header) throws Exception {
int val = 0;
switch (header) {
case CURRENT_STATE_HDR:
Pattern pattern = Pattern.compile("mCurrentState: CpmsState "
+ "[^\\n]*carPowerStateListenerState=(\\d+)");
Matcher matcher = pattern.matcher(mLines[mIdx]);
if (!matcher.find()) {
throw new IllegalArgumentException("malformatted mCurrentState: "
+ mLines[mIdx]);
}
val = Integer.parseInt(matcher.group(1));
break;
case NUMBER_POLICY_LISTENERS_HDR:
int strLen = mLines[mIdx].length();
val = Integer.parseInt(mLines[mIdx].substring(strLen - 1).trim());
break;
default:
break;
}
return val;
}
private String getStringData(String header) {
String val = null;
if (mLines[mIdx].trim().length() != header.length()) {
val = mLines[mIdx].trim().substring(header.length()).trim();
}
return val;
}
private ArrayList<String> getStringArray(String startHdr, String endHdr)
throws Exception {
if (!mLines[mIdx].contains(startHdr)) {
String errMsg = String.format("expected start header %s at line %d : %s",
startHdr, mIdx, mLines[mIdx]);
throw new IllegalArgumentException(errMsg);
}
ArrayList<String> strArray = new ArrayList<String>();
while (++mIdx < mLines.length && !mLines[mIdx].contains(endHdr)) {
strArray.add(mLines[mIdx]);
}
mIdx--;
if (mIdx == (mLines.length - 1)) {
throw new IllegalArgumentException("reaches the end while get " + startHdr);
}
return strArray;
}
private void parseComponentStates(String startHdr, String endHdr,
boolean hasStateInfo) throws Exception {
mEnables = new ArrayList<String>();
mDisables = new ArrayList<String>();
while (mIdx < (mLines.length - 1) && !mLines[++mIdx].contains(endHdr)) {
String stateStr = mLines[mIdx].trim();
String[] vals = stateStr.split(":\\s");
if (hasStateInfo && vals.length != 2) {
String errMsg = String.format("wrong format at %d in: %s ", mIdx, stateStr);
CLog.e(errMsg);
throw new IllegalArgumentException(errMsg);
}
for (int i = 0; i < vals.length; i++) {
vals[i] = vals[i].trim();
}
if (!COMPONENT_SET.contains(vals[0])) {
String errMsg = String.format("invalid component at %d with %s in: %s",
mIdx, vals[0], stateStr);
CLog.e(errMsg);
throw new IllegalArgumentException(errMsg);
}
if (hasStateInfo) {
if (vals[1].startsWith("on")) {
mEnables.add(vals[0]);
} else if (vals[1].startsWith("off")) {
mDisables.add(vals[0]);
} else {
String errMsg =
String.format("wrong state value at %d with (%s, %s) in: %s",
mIdx, vals[0], vals[1], stateStr);
CLog.e(errMsg);
throw new IllegalArgumentException(errMsg);
}
} else {
mDisables.add(vals[0]);
}
}
mIdx--;
if (mIdx == (mLines.length - 1)) {
throw new IllegalArgumentException("reaches the end while parse " + startHdr);
}
}
private String[] getChangedComponents(String startHdr, String endHdr) {
int idx = mLines[mIdx].indexOf(endHdr);
String compStr;
if (idx < 0) {
compStr = mLines[mIdx].substring(startHdr.length());
} else {
compStr = mLines[mIdx].substring(startHdr.length(), idx);
mLines[mIdx] = mLines[mIdx].substring(idx);
mIdx--;
}
return compStr.split(",\\s*");
}
private boolean getBooleanData(String header) {
return Boolean.parseBoolean(mLines[mIdx].trim().substring(header.length()).trim());
}
private String searchHeader() {
String header = null;
for (mIdx++; mIdx < mLines.length; mIdx++) {
if (mLines[mIdx].trim().isEmpty()) {
continue;
}
int firstHdrPos = mLines[mIdx].length() + 1;
for (int i = 0; i < HEADERS.length; i++) {
int tempHdrPos = mLines[mIdx].indexOf(HEADERS[i]);
if (tempHdrPos >= 0 && (firstHdrPos > tempHdrPos)) {
firstHdrPos = tempHdrPos;
header = HEADERS[i];
}
}
if (header != null) {
break;
}
}
return header;
}
}
}