blob: afd43597a7057601e3d4c26df690d394388348a9 [file] [log] [blame]
/*
* Copyright (C) 2013 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 com.android.loganalysis.parser;
import com.android.loganalysis.item.DumpsysBatteryInfoItem;
import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A {@link IParser} to handle the "dumpsys batteryinfo" command output.
*/
public class DumpsysBatteryInfoParser implements IParser {
private static final Pattern LAST_CHARGED_START_PAT = Pattern.compile(
"^Statistics since last charge:$");
private static final Pattern LAST_UNPLUGGED_START_PAT = Pattern.compile(
"^Statistics since last unplugged:$");
private static final Pattern WAKE_LOCK_START_PAT = Pattern.compile(
"^ All partial wake locks:$");
private static final String WAKE_LOCK_PAT_SUFFIX =
"((\\d+)d )?((\\d+)h )?((\\d+)m )?((\\d+)s )?((\\d+)ms )?\\((\\d+) times\\) realtime";
/**
* Match a valid line such as:
* " Kernel Wake lock \"Process\": 1d 2h 3m 4s 5ms (6 times) realtime";
*/
private static final Pattern KERNEL_WAKE_LOCK_PAT = Pattern.compile(
"^ Kernel Wake lock \"([^\"]+)\": " + WAKE_LOCK_PAT_SUFFIX);
/**
* Match a valid line such as:
* " Wake lock #1234 Process: 1d 2h 3m 4s 5ms (6 times) realtime";
*/
private static final Pattern WAKE_LOCK_PAT = Pattern.compile(
"^ Wake lock #(\\d+) (.+): " + WAKE_LOCK_PAT_SUFFIX);
private DumpsysBatteryInfoItem mItem = new DumpsysBatteryInfoItem();
/**
* {@inheritDoc}
*/
@Override
public DumpsysBatteryInfoItem parse(List<String> lines) {
WakeLockCategory kernelWakeLockCategory = null;
WakeLockCategory wakeLockCategory = null;
boolean inKernelWakeLock = false;
boolean inWakeLock = false;
// Look for the section for last unplugged statistics. Kernel wakelocks are in the lines
// immediately following, until a blank line. Partial wake locks are in their own block,
// until a blank line. Return immediately after since there is nothing left to parse.
for (String line : lines) {
if (kernelWakeLockCategory == null || wakeLockCategory == null) {
Matcher m = LAST_CHARGED_START_PAT.matcher(line);
if (m.matches()) {
kernelWakeLockCategory = WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK;
wakeLockCategory = WakeLockCategory.LAST_CHARGE_WAKELOCK;
inKernelWakeLock = true;
}
m = LAST_UNPLUGGED_START_PAT.matcher(line);
if (m.matches()) {
kernelWakeLockCategory = WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK;
wakeLockCategory = WakeLockCategory.LAST_UNPLUGGED_WAKELOCK;
inKernelWakeLock = true;
}
} else {
if (inKernelWakeLock) {
if ("".equals(line.trim())) {
inKernelWakeLock = false;
} else {
parseKernelWakeLock(line, kernelWakeLockCategory);
}
} else if (inWakeLock) {
if ("".equals(line.trim())) {
inWakeLock = false;
kernelWakeLockCategory = null;
wakeLockCategory = null;
} else {
parseWakeLock(line, wakeLockCategory);
}
} else {
Matcher m = WAKE_LOCK_START_PAT.matcher(line);
if (m.matches()) {
inWakeLock = true;
}
}
}
}
return mItem;
}
/**
* Parse a line of output and add it to the last unplugged kernel wake lock section.
* <p>
* Exposed for unit testing.
* </p>
*/
void parseKernelWakeLock(String line, WakeLockCategory category) {
Matcher m = KERNEL_WAKE_LOCK_PAT.matcher(line);
if (!m.matches()) {
return;
}
final String name = m.group(1);
final long days = parseLongOrZero(m.group(3));
final long hours = parseLongOrZero(m.group(5));
final long mins = parseLongOrZero(m.group(7));
final long secs = parseLongOrZero(m.group(9));
final long msecs = parseLongOrZero(m.group(11));
final int timesCalled = Integer.parseInt(m.group(12));
mItem.addWakeLock(name, getMs(days, hours, mins, secs, msecs), timesCalled, category);
}
/**
* Parse a line of output and add it to the last unplugged wake lock section.
* <p>
* Exposed for unit testing.
* </p>
*/
void parseWakeLock(String line, WakeLockCategory category) {
Matcher m = WAKE_LOCK_PAT.matcher(line);
if (!m.matches()) {
return;
}
final int number = Integer.parseInt(m.group(1));
final String name = m.group(2);
final long days = parseLongOrZero(m.group(4));
final long hours = parseLongOrZero(m.group(6));
final long mins = parseLongOrZero(m.group(8));
final long secs = parseLongOrZero(m.group(10));
final long msecs = parseLongOrZero(m.group(12));
final int timesCalled = Integer.parseInt(m.group(13));
mItem.addWakeLock(name, number, getMs(days, hours, mins, secs, msecs), timesCalled,
category);
}
/**
* Get the {@link DumpsysBatteryInfoItem}.
* <p>
* Exposed for unit testing.
* </p>
*/
DumpsysBatteryInfoItem getItem() {
return mItem;
}
/**
* Convert days/hours/mins/secs/msecs into milliseconds.
* <p>
* Exposed for unit testing.
* </p>
*/
static long getMs(long days, long hours, long mins, long secs, long msecs) {
return (((24 * days + hours) * 60 + mins) * 60 + secs) * 1000 + msecs;
}
/**
* Parses a string into a long, or returns 0 if the string is null.
*
* @param s a {@link String} containing the long representation to be parsed
* @return the long represented by the argument in decimal, or 0 if the string is {@code null}.
* @throws NumberFormatException if the string is not {@code null} or does not contain a
* parsable long.
*/
private long parseLongOrZero(String s) {
if (s == null) {
return 0;
}
return Long.parseLong(s);
}
}