summaryrefslogtreecommitdiff
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/loganalysis/LogAnalyzer.java91
-rw-r--r--src/com/android/loganalysis/item/BatteryDischargeItem.java137
-rw-r--r--src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java156
-rw-r--r--src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java69
-rw-r--r--src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java97
-rw-r--r--src/com/android/loganalysis/item/DumpsysItem.java35
-rw-r--r--src/com/android/loganalysis/item/DumpsysProcStatsItem.java24
-rw-r--r--src/com/android/loganalysis/item/InterruptItem.java162
-rw-r--r--src/com/android/loganalysis/item/ProcessUsageItem.java199
-rw-r--r--src/com/android/loganalysis/item/WakelockItem.java (renamed from src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java)103
-rw-r--r--src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java165
-rw-r--r--src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java181
-rw-r--r--src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java189
-rw-r--r--src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java89
-rw-r--r--src/com/android/loganalysis/parser/DumpsysParser.java23
-rw-r--r--src/com/android/loganalysis/parser/DumpsysProcStatsParser.java51
-rw-r--r--src/com/android/loganalysis/parser/InterruptParser.java89
-rw-r--r--src/com/android/loganalysis/parser/ProcessUsageParser.java99
-rw-r--r--src/com/android/loganalysis/parser/WakelockParser.java155
-rw-r--r--src/com/android/loganalysis/rule/AbstractPowerRule.java67
-rw-r--r--src/com/android/loganalysis/rule/IRule.java29
-rw-r--r--src/com/android/loganalysis/rule/ProcessUsageRule.java76
-rw-r--r--src/com/android/loganalysis/rule/RuleEngine.java70
-rw-r--r--src/com/android/loganalysis/rule/WakelockRule.java71
-rw-r--r--src/com/android/loganalysis/util/NumberFormattingUtil.java94
25 files changed, 2270 insertions, 251 deletions
diff --git a/src/com/android/loganalysis/LogAnalyzer.java b/src/com/android/loganalysis/LogAnalyzer.java
index 787e58a..a0a49a1 100644
--- a/src/com/android/loganalysis/LogAnalyzer.java
+++ b/src/com/android/loganalysis/LogAnalyzer.java
@@ -26,10 +26,14 @@ import com.android.loganalysis.parser.KernelLogParser;
import com.android.loganalysis.parser.LogcatParser;
import com.android.loganalysis.parser.MemoryHealthParser;
import com.android.loganalysis.parser.MonkeyLogParser;
+import com.android.loganalysis.rule.RuleEngine;
+import com.android.loganalysis.rule.RuleEngine.RuleType;
import com.android.loganalysis.util.config.ArgsOptionParser;
import com.android.loganalysis.util.config.ConfigurationException;
import com.android.loganalysis.util.config.Option;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
@@ -39,6 +43,9 @@ import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A command line tool to parse a bugreport, logcat, or kernel log file and return the output.
*/
@@ -49,6 +56,10 @@ public class LogAnalyzer {
JSON;
}
+ private enum ResultType {
+ RAW, ANALYSIS;
+ }
+
@Option(name="bugreport", description="The path to the bugreport")
private String mBugreportPath = null;
@@ -67,6 +78,17 @@ public class LogAnalyzer {
@Option(name="output", description="The output format, currently only JSON")
private OutputFormat mOutputFormat = OutputFormat.JSON;
+ @Option(name="rule-type", description="The type of rules to be applied")
+ private RuleType mRuleType = RuleType.ALL;
+
+ @Option(name="print", description="Print the result type")
+ private List<ResultType> mResultType = new ArrayList<ResultType>();
+
+ /** Constant for JSON output */
+ private static final String RAW_DATA = "RAW";
+ /** Constant for JSON output */
+ private static final String ANALYSIS_DATA = "ANALYSIS";
+
/**
* Run the command line tool
*/
@@ -140,9 +162,61 @@ public class LogAnalyzer {
*/
private void printBugreport(BugreportItem bugreport) {
if (OutputFormat.JSON.equals(mOutputFormat)) {
- printJson(bugreport);
+ if (mResultType.size() == 0) {
+ printJson(bugreport);
+ } else if (mResultType.size() == 1) {
+ switch (mResultType.get(0)) {
+ case RAW:
+ printJson(bugreport);
+ break;
+ case ANALYSIS:
+ printBugreportAnalysis(getBugreportAnalysis(bugreport));
+ break;
+ default:
+ // should not get here
+ return;
+ }
+ } else {
+ JSONObject result = new JSONObject();
+ try {
+ for (ResultType resultType : mResultType) {
+ switch (resultType) {
+ case RAW:
+ result.put(RAW_DATA, bugreport.toJson());
+ break;
+ case ANALYSIS:
+ result.put(ANALYSIS_DATA, getBugreportAnalysis(bugreport));
+ break;
+ default:
+ // should not get here
+ break;
+ }
+ }
+ } catch (JSONException e) {
+ // Ignore
+ }
+ printJson(result);
+ }
+ }
+ }
+
+ private JSONArray getBugreportAnalysis(BugreportItem bugreport) {
+ RuleEngine ruleEngine = new RuleEngine(bugreport);
+ ruleEngine.registerRules(mRuleType);
+ ruleEngine.executeRules();
+ if (ruleEngine.getAnalysis() != null) {
+ return ruleEngine.getAnalysis();
+ } else {
+ return new JSONArray();
+ }
+ }
+
+ private void printBugreportAnalysis(JSONArray analysis) {
+ if (analysis != null && analysis.length() > 0) {
+ System.out.println(analysis.toString());
+ } else {
+ System.out.println(new JSONObject().toString());
}
- // TODO: Print bugreport in human readable form.
}
/**
@@ -180,7 +254,18 @@ public class LogAnalyzer {
*/
private void printJson(IItem item) {
if (item != null && item.toJson() != null) {
- System.out.println(item.toJson().toString());
+ printJson(item.toJson());
+ } else {
+ printJson(new JSONObject());
+ }
+ }
+
+ /**
+ * Print an {@link JSONObject} to stdout
+ */
+ private void printJson(JSONObject json) {
+ if (json != null) {
+ System.out.println(json.toString());
} else {
System.out.println(new JSONObject().toString());
}
diff --git a/src/com/android/loganalysis/item/BatteryDischargeItem.java b/src/com/android/loganalysis/item/BatteryDischargeItem.java
new file mode 100644
index 0000000..cdfcbcc
--- /dev/null
+++ b/src/com/android/loganalysis/item/BatteryDischargeItem.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+
+/**
+ * An {@link IItem} used to store information related to Battery discharge
+ */
+public class BatteryDischargeItem implements IItem {
+
+ /** Constant for JSON output */
+ public static final String BATTERY_DISCHARGE = "BATTERY_DISCHARGE";
+
+ private Collection<BatteryDischargeInfoItem> mBatteryDischargeInfo =
+ new LinkedList<BatteryDischargeInfoItem>();
+
+ public static class BatteryDischargeInfoItem extends GenericItem {
+ /** Constant for JSON output */
+ public static final String CLOCK_TIME_OF_DISCHARGE = "CLOCK_TIME_OF_DISCHARGE";
+ /** Constant for JSON output */
+ public static final String DISCHARGE_ELAPSED_TIME = "DISCHARGE_ELAPSED_TIME";
+ /** Constant for JSON output */
+ public static final String BATTERY_LEVEL = "BATTERY_LEVEL";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ CLOCK_TIME_OF_DISCHARGE, DISCHARGE_ELAPSED_TIME, BATTERY_LEVEL));
+
+ /**
+ * The constructor for {@link BatteryDischargeInfoItem}
+ *
+ * @param clockTime Clock time when the battery discharge happened
+ * @param elapsedTime Time it took to discharge to the current battery level
+ * @param batteryLevel Current battery level
+ */
+ public BatteryDischargeInfoItem(Calendar clockTime, long elapsedTime, int batteryLevel) {
+ super(ATTRIBUTES);
+
+ setAttribute(CLOCK_TIME_OF_DISCHARGE, clockTime);
+ setAttribute(DISCHARGE_ELAPSED_TIME, elapsedTime);
+ setAttribute(BATTERY_LEVEL, batteryLevel);
+ }
+
+ /**
+ * Get the clock time when the battery level dropped
+ */
+ public Calendar getClockTime() {
+ return (Calendar) getAttribute(CLOCK_TIME_OF_DISCHARGE);
+ }
+
+ /**
+ * Get the time elapsed to discharge to the current battery level
+ */
+ public long getElapsedTime() {
+ return (long) getAttribute(DISCHARGE_ELAPSED_TIME);
+ }
+
+ /**
+ * Get the current battery level
+ */
+ public int getBatteryLevel() {
+ return (int) getAttribute(BATTERY_LEVEL);
+ }
+ }
+
+ /**
+ * Add a battery discharge step from battery stats
+ *
+ * @param clockTime Clock time when the battery discharge happened
+ * @param elapsedTime Time it took to discharge to the current battery level
+ * @param batteryLevel Current battery level
+ */
+ public void addBatteryDischargeInfo(Calendar clockTime, long elapsedTime, int batteryLevel) {
+ mBatteryDischargeInfo.add(new BatteryDischargeInfoItem(clockTime,
+ elapsedTime, batteryLevel));
+ }
+
+ public Collection<BatteryDischargeInfoItem> getDischargeStepsInfo() {
+ return mBatteryDischargeInfo;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ throw new ConflictingItemException("Wakelock items cannot be merged");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConsistent(IItem other) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject object = new JSONObject();
+ try {
+ JSONArray batteryDischargeSteps = new JSONArray();
+ for (BatteryDischargeInfoItem batteryDischargeStep : mBatteryDischargeInfo) {
+ batteryDischargeSteps.put(batteryDischargeStep.toJson());
+ }
+ object.put(BATTERY_DISCHARGE, batteryDischargeSteps);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return object;
+ }
+}
diff --git a/src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java b/src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java
new file mode 100644
index 0000000..a1ac0b0
--- /dev/null
+++ b/src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * An {@link IItem} used to store BatteryStats Info
+ */
+public class BatteryStatsDetailedInfoItem implements IItem {
+
+ /** Constant for JSON output */
+ public static final String TIME_ON_BATTERY = "TIME_ON_BATTERY";
+ /** Constant for JSON output */
+ public static final String SCREEN_ON_TIME = "SCREEN_ON_TIME";
+ /** Constant for JSON output */
+ public static final String WAKELOCKS = "WAKELOCKS";
+ /** Constant for JSON output */
+ public static final String INTERRUPTS = "INTERRUPTS";
+ /** Constant for JSON output */
+ public static final String PROCESS_USAGE = "PROCESS_USAGE";
+
+ private long mTimeOnBattery = 0;
+ private long mScreenOnTime = 0;
+ private WakelockItem mWakelockItem = null;
+ private InterruptItem mInterruptItem = null;
+ private ProcessUsageItem mprocessUsageItem = null;
+
+ /**
+ * Set the time on battery
+ */
+ public void setTimeOnBattery(long timeOnBattery) {
+ mTimeOnBattery = timeOnBattery;
+ }
+
+ /**
+ * Set the time on battery
+ */
+ public void setScreenOnTime(long screenOnTime) {
+ mScreenOnTime = screenOnTime;
+ }
+
+ /**
+ * Set the wakelock summary {@link WakelockItem}
+ */
+ public void setWakelockItem(WakelockItem wakelockItem) {
+ mWakelockItem = wakelockItem;
+ }
+
+ /**
+ * Set the interrupt summary {@link InterruptItem}
+ */
+ public void setInterruptItem(InterruptItem interruptItem) {
+ mInterruptItem = interruptItem;
+ }
+
+ /**
+ * Set the process usage {@link ProcessUsageItem}
+ */
+ public void setProcessUsageItem(ProcessUsageItem processUsageItem) {
+ mprocessUsageItem = processUsageItem;
+ }
+
+ /**
+ * Get the time on battery
+ */
+ public long getTimeOnBattery() {
+ return mTimeOnBattery;
+ }
+
+ /**
+ * Get the screen on time
+ */
+ public long getScreenOnTime() {
+ return mScreenOnTime;
+ }
+
+ /**
+ * Get the wakelock summary {@link WakelockItem}
+ */
+ public WakelockItem getWakelockItem() {
+ return mWakelockItem;
+ }
+
+ /**
+ * Get the interrupt summary {@link InterruptItem}
+ */
+ public InterruptItem getInterruptItem() {
+ return mInterruptItem;
+ }
+
+ /**
+ * Get the process usage summary {@link ProcessUsageItem}
+ */
+ public ProcessUsageItem getProcessUsageItem() {
+ return mprocessUsageItem;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ throw new ConflictingItemException("Dumpsys battery info items cannot be merged");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConsistent(IItem other) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject batteryStatsComponent = new JSONObject();
+ try {
+ if (mTimeOnBattery > 0) {
+ batteryStatsComponent.put(TIME_ON_BATTERY, getTimeOnBattery());
+ }
+ if (mScreenOnTime > 0) {
+ batteryStatsComponent.put(SCREEN_ON_TIME, getScreenOnTime());
+ }
+ if (mWakelockItem != null) {
+ batteryStatsComponent.put(WAKELOCKS, mWakelockItem.toJson());
+ }
+ if (mInterruptItem != null) {
+ batteryStatsComponent.put(INTERRUPTS, mInterruptItem.toJson());
+ }
+ if (mprocessUsageItem != null) {
+ batteryStatsComponent.put(PROCESS_USAGE, mprocessUsageItem.toJson());
+ }
+ } catch (JSONException e) {
+ // ignore
+ }
+ return batteryStatsComponent;
+ }
+}
diff --git a/src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java b/src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java
new file mode 100644
index 0000000..6b07905
--- /dev/null
+++ b/src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An {@link GenericItem} used to store the power analysis summary
+ */
+public class BatteryStatsSummaryInfoItem extends GenericItem {
+
+ /** Constant for JSON output */
+ public static final String DISCHARGE_RATE = "DISCHARGE_RATE";
+ /** Constant for JSON output */
+ public static final String PEAK_DISCHARGE_TIME = "PEAK_DISCHARGE_TIME";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ DISCHARGE_RATE, PEAK_DISCHARGE_TIME));
+
+ /**
+ * The constructor for {@link BatteryStatsSummaryInfoItem}.
+ */
+ public BatteryStatsSummaryInfoItem() {
+ super(ATTRIBUTES);
+ }
+
+ /**
+ * Get the battery discharge rate
+ */
+ public String getBatteryDischargeRate() {
+ return (String) getAttribute(DISCHARGE_RATE);
+ }
+
+ /**
+ * Set the battery discharge rate
+ */
+ public void setBatteryDischargeRate(String dischargeRate) {
+ setAttribute(DISCHARGE_RATE, dischargeRate);
+ }
+
+ /**
+ * Get the peak discharge time
+ */
+ public String getPeakDischargeTime() {
+ return (String) getAttribute(PEAK_DISCHARGE_TIME);
+ }
+
+ /**
+ * Set the peak discharge time
+ */
+ public void setPeakDischargeTime(String peakDischargeTime) {
+ setAttribute(PEAK_DISCHARGE_TIME, peakDischargeTime);
+ }
+}
diff --git a/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java b/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java
new file mode 100644
index 0000000..1039324
--- /dev/null
+++ b/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * An {@link IItem} used to store BatteryStats Info
+ */
+public class DumpsysBatteryStatsItem implements IItem {
+
+ /** Constant for JSON output */
+ public static final String SUMMARY = "SUMMARY";
+ /** Constant for JSON output */
+ public static final String DETAILED_STATS = "DETAILED_STATS";
+
+ private BatteryStatsSummaryInfoItem mBatteryStatsSummaryItem;
+ private BatteryStatsDetailedInfoItem mDetailedBatteryStatsItem;
+
+ /**
+ * Set the battery stats summary {@link BatteryStatsSummaryInfoItem}
+ */
+ public void setBatteryStatsSummarytem(BatteryStatsSummaryInfoItem summaryItem) {
+ mBatteryStatsSummaryItem = summaryItem;
+ }
+
+ /**
+ * Set the detailed battery stats item {@link BatteryStatsDetailedInfoItem}
+ */
+ public void setDetailedBatteryStatsItem(BatteryStatsDetailedInfoItem detailedItem) {
+ mDetailedBatteryStatsItem = detailedItem;
+ }
+
+ /**
+ * Get the battery stats summary {@link BatteryStatsSummaryInfoItem}
+ */
+ public BatteryStatsSummaryInfoItem getBatteryStatsSummaryItem() {
+ return mBatteryStatsSummaryItem;
+ }
+
+ /**
+ * Get the detailed battery stats item {@link BatteryStatsDetailedInfoItem}
+ */
+ public BatteryStatsDetailedInfoItem getDetailedBatteryStatsItem() {
+ return mDetailedBatteryStatsItem;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ throw new ConflictingItemException("Dumpsys battery info items cannot be merged");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConsistent(IItem other) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject batteryStatsComponent = new JSONObject();
+ try {
+ if (mBatteryStatsSummaryItem != null) {
+ batteryStatsComponent.put(SUMMARY, mBatteryStatsSummaryItem.toJson());
+ }
+ if (mDetailedBatteryStatsItem != null) {
+ batteryStatsComponent.put(DETAILED_STATS, mDetailedBatteryStatsItem.toJson());
+ }
+ } catch (JSONException e) {
+ // ignore
+ }
+ return batteryStatsComponent;
+ }
+}
diff --git a/src/com/android/loganalysis/item/DumpsysItem.java b/src/com/android/loganalysis/item/DumpsysItem.java
index 866c56f..5bd82e2 100644
--- a/src/com/android/loganalysis/item/DumpsysItem.java
+++ b/src/com/android/loganalysis/item/DumpsysItem.java
@@ -25,28 +25,45 @@ import java.util.Set;
public class DumpsysItem extends GenericItem {
/** Constant for JSON output */
- private static final String BATTERY_INFO = "BATTERY_INFO";
+ private static final String BATTERY_STATS = "BATTERY_STATS";
+ /** Constant for JSON output */
+ private static final String PROC_STATS = "PROC_STATS";
- private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(BATTERY_INFO));
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ BATTERY_STATS, PROC_STATS));
/**
- * The constructor for {@link BugreportItem}.
+ * The constructor for {@link DumpsysItem}.
*/
public DumpsysItem() {
super(ATTRIBUTES);
}
/**
- * Get the battery info section of the dumpsys.
+ * Set the {@link DumpsysBatteryStatsItem} of the bugreport.
+ */
+ public void setBatteryInfo(DumpsysBatteryStatsItem batteryStats) {
+ setAttribute(BATTERY_STATS, batteryStats);
+ }
+
+ /**
+ * Set the {@link DumpsysProcStatsItem} of the bugreport.
+ */
+ public void setProcStats(DumpsysProcStatsItem procStats) {
+ setAttribute(PROC_STATS, procStats);
+ }
+
+ /**
+ * Get the {@link DumpsysBatteryStatsItem} of the bugreport.
*/
- public DumpsysBatteryInfoItem getBatteryInfo() {
- return (DumpsysBatteryInfoItem) getAttribute(BATTERY_INFO);
+ public DumpsysBatteryStatsItem getBatteryStats() {
+ return (DumpsysBatteryStatsItem) getAttribute(BATTERY_STATS);
}
/**
- * Set the battery info section of the dumpsys.
+ * Get the {@link DumpsysProcStatsItem} of the bugreport.
*/
- public void setBatteryInfo(DumpsysBatteryInfoItem batteryInfo) {
- setAttribute(BATTERY_INFO, batteryInfo);
+ public DumpsysProcStatsItem getProcStats() {
+ return (DumpsysProcStatsItem) getAttribute(PROC_STATS);
}
}
diff --git a/src/com/android/loganalysis/item/DumpsysProcStatsItem.java b/src/com/android/loganalysis/item/DumpsysProcStatsItem.java
new file mode 100644
index 0000000..846f70a
--- /dev/null
+++ b/src/com/android/loganalysis/item/DumpsysProcStatsItem.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+/**
+ * An {@link IItem} used to store uid and processname map. It is going to be
+ * used a helper item and not going to be marshalled to JSON
+ */
+public class DumpsysProcStatsItem extends GenericMapItem<String> {
+
+}
diff --git a/src/com/android/loganalysis/item/InterruptItem.java b/src/com/android/loganalysis/item/InterruptItem.java
new file mode 100644
index 0000000..1169004
--- /dev/null
+++ b/src/com/android/loganalysis/item/InterruptItem.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An {@link IItem} used to store information related to interrupts
+ */
+public class InterruptItem implements IItem {
+ /** Constant for JSON output */
+ public static final String INTERRUPTS = "INTERRUPT_INFO";
+
+ private Collection<InterruptInfoItem> mInterrupts = new LinkedList<InterruptInfoItem>();
+
+ /**
+ * Enum for describing the type of interrupt
+ */
+ public enum InterruptCategory {
+ WIFI_INTERRUPT,
+ MODEM_INTERRUPT,
+ ALARM_INTERRUPT,
+ ADSP_INTERRUPT,
+ UNKNOWN_INTERRUPT,
+ }
+
+ public static class InterruptInfoItem extends GenericItem {
+ /** Constant for JSON output */
+ public static final String NAME = "NAME";
+ /** Constant for JSON output */
+ public static final String CATEGORY = "CATEGORY";
+ /** Constant for JSON output */
+ public static final String INTERRUPT_COUNT = "INTERRUPT_COUNT";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ NAME, INTERRUPT_COUNT, CATEGORY));
+
+ /**
+ * The constructor for {@link InterruptItem}
+ *
+ * @param name The name of the wake lock
+ * @param interruptCount The number of times the interrupt woke up the AP
+ * @param category The {@link InterruptCategory} of the interrupt
+ */
+ public InterruptInfoItem(String name, int interruptCount,
+ InterruptCategory category) {
+ super(ATTRIBUTES);
+
+ setAttribute(NAME, name);
+ setAttribute(INTERRUPT_COUNT, interruptCount);
+ setAttribute(CATEGORY, category);
+ }
+
+ /**
+ * Get the name of the interrupt
+ */
+ public String getName() {
+ return (String) getAttribute(NAME);
+ }
+
+ /**
+ * Get the interrupt count.
+ */
+ public int getInterruptCount() {
+ return (Integer) getAttribute(INTERRUPT_COUNT);
+ }
+
+ /**
+ * Get the {@link InterruptCategory} of the wake lock.
+ */
+ public InterruptCategory getCategory() {
+ return (InterruptCategory) getAttribute(CATEGORY);
+ }
+ }
+
+ /**
+ * Add an interrupt from the battery info section.
+ *
+ * @param name The name of the interrupt
+ * @param interruptCount Number of interrupts
+ * @param category The {@link InterruptCategory} of the interrupt.
+ */
+ public void addInterrupt(String name, int interruptCount,
+ InterruptCategory category) {
+ mInterrupts.add(new InterruptInfoItem(name, interruptCount, category));
+ }
+
+ /**
+ * Get a list of {@link InterruptInfoItem} objects matching a given {@link InterruptCategory}.
+ */
+ public List<InterruptInfoItem> getInterrupts(InterruptCategory category) {
+ LinkedList<InterruptInfoItem> interrupts = new LinkedList<InterruptInfoItem>();
+ if (category == null) {
+ return interrupts;
+ }
+
+ for (InterruptInfoItem interrupt : mInterrupts) {
+ if (category.equals(interrupt.getCategory())) {
+ interrupts.add(interrupt);
+ }
+ }
+ return interrupts;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ throw new ConflictingItemException("Wakelock items cannot be merged");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConsistent(IItem other) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject object = new JSONObject();
+ if (mInterrupts != null) {
+ try {
+ JSONArray interrupts = new JSONArray();
+ for (InterruptInfoItem interrupt : mInterrupts) {
+ interrupts.put(interrupt.toJson());
+ }
+ object.put(INTERRUPTS, interrupts);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ }
+ return object;
+ }
+}
diff --git a/src/com/android/loganalysis/item/ProcessUsageItem.java b/src/com/android/loganalysis/item/ProcessUsageItem.java
new file mode 100644
index 0000000..e2feddb
--- /dev/null
+++ b/src/com/android/loganalysis/item/ProcessUsageItem.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2015 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.item;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+
+/**
+ * An {@link IItem} used to store information related to network bandwidth, sensor usage,
+ * alarm usage by each processes
+ */
+public class ProcessUsageItem implements IItem {
+
+ /** Constant for JSON output */
+ public static final String PROCESS_USAGE = "PROCESS_USAGE";
+
+ private Collection<ProcessUsageInfoItem> mProcessUsage =
+ new LinkedList<ProcessUsageInfoItem>();
+
+ public static class SensorInfoItem extends GenericItem {
+ /** Constant for JSON output */
+ public static final String SENSOR_NAME = "SENSOR_NAME";
+ /** Constant for JSON output */
+ public static final String USAGE_DURATION = "USAGE_DURATION";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ SENSOR_NAME, USAGE_DURATION));
+
+ /**
+ * The constructor for {@link SensorInfoItem}
+ *
+ * @param name The name of the sensor
+ * @param usageDuration Duration of the usage
+ */
+ public SensorInfoItem(String name, long usageDuration) {
+ super(ATTRIBUTES);
+
+ setAttribute(SENSOR_NAME, name);
+ setAttribute(USAGE_DURATION, usageDuration);
+ }
+
+ /**
+ * Get the sensor name
+ */
+ public String getSensorName() {
+ return (String) getAttribute(SENSOR_NAME);
+ }
+
+ /**
+ * Get the sensor usage duration in milliseconds
+ */
+ public long getUsageDurationMs() {
+ return (long) getAttribute(USAGE_DURATION);
+ }
+ }
+
+ public static class ProcessUsageInfoItem extends GenericItem {
+ /** Constant for JSON output */
+ public static final String ALARM_WAKEUPS = "ALARM_WAKEUPS";
+ /** Constant for JSON output */
+ public static final String SENSOR_USAGE = "SENSOR_USAGE";
+ /** Constant for JSON output */
+ public static final String PROCESS_UID = "PROCESS_UID";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ ALARM_WAKEUPS, SENSOR_USAGE, PROCESS_UID));
+
+ /**
+ * The constructor for {@link ProcessUsageItem}
+ *
+ * @param uid The name of the process
+ * @param alarmWakeups Number of alarm wakeups
+ * @param sensorUsage Different sensors used by the process
+ */
+ public ProcessUsageInfoItem(String uid, int alarmWakeups,
+ LinkedList<SensorInfoItem> sensorUsage) {
+ super(ATTRIBUTES);
+
+ setAttribute(PROCESS_UID, uid);
+ setAttribute(ALARM_WAKEUPS, alarmWakeups);
+ setAttribute(SENSOR_USAGE, sensorUsage);
+ }
+
+ /**
+ * Get the number of Alarm wakeups
+ */
+ public int getAlarmWakeups() {
+ return (int) getAttribute(ALARM_WAKEUPS);
+ }
+
+ /**
+ * Get the Sensor usage of the process
+ */
+ @SuppressWarnings("unchecked")
+ public LinkedList<SensorInfoItem> getSensorUsage() {
+ return (LinkedList<SensorInfoItem>) getAttribute(SENSOR_USAGE);
+ }
+
+ /**
+ * Get the process name
+ */
+ public String getProcessUID() {
+ return (String) getAttribute(PROCESS_UID);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject object = new JSONObject();
+ try {
+ object.put(PROCESS_UID, getProcessUID());
+ JSONArray sensorUsage = new JSONArray();
+ for (SensorInfoItem usage : getSensorUsage()) {
+ sensorUsage.put(usage.toJson());
+ }
+ object.put(SENSOR_USAGE, sensorUsage);
+ object.put(ALARM_WAKEUPS, getAlarmWakeups());
+
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return object;
+ }
+ }
+
+ /**
+ * Add individual process usage from the battery stats section.
+ *
+ * @param processUID The name of the process
+ * @param alarmWakeups The number of alarm wakeups
+ * @param sensorUsage Sensor usage of the process
+ */
+ public void addProcessUsage(String processUID, int alarmWakeups,
+ LinkedList<SensorInfoItem> sensorUsage) {
+ mProcessUsage.add(new ProcessUsageInfoItem(processUID, alarmWakeups, sensorUsage));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ throw new ConflictingItemException("Wakelock items cannot be merged");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConsistent(IItem other) {
+ return false;
+ }
+
+ public Collection<ProcessUsageInfoItem> getProcessUsage() {
+ return mProcessUsage;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject object = new JSONObject();
+ if (mProcessUsage != null) {
+ try {
+ JSONArray processUsage = new JSONArray();
+ for (ProcessUsageInfoItem usage : mProcessUsage) {
+ processUsage.put(usage.toJson());
+ }
+ object.put(PROCESS_USAGE, processUsage);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ }
+ return object;
+ }
+}
diff --git a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java b/src/com/android/loganalysis/item/WakelockItem.java
index 8525f94..7bfab4b 100644
--- a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
+++ b/src/com/android/loganalysis/item/WakelockItem.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -27,32 +27,30 @@ import java.util.List;
import java.util.Set;
/**
- * An {@link IItem} used to store the battery info part of the dumpsys output.
+ * An {@link IItem} used to store information related to wake locks and kernel wake locks
*/
-public class DumpsysBatteryInfoItem implements IItem {
+public class WakelockItem implements IItem {
/** Constant for JSON output */
- public static final String WAKELOCKS = "WAKELOCKS";
+ public static final String WAKELOCKS = "WAKELOCKS_INFO";
+
+ private Collection<WakelockInfoItem> mWakeLocks = new LinkedList<WakelockInfoItem>();
/**
* Enum for describing the type of wakelock
*/
public enum WakeLockCategory {
- LAST_CHARGE_WAKELOCK,
- LAST_CHARGE_KERNEL_WAKELOCK,
- LAST_UNPLUGGED_WAKELOCK,
- LAST_UNPLUGGED_KERNEL_WAKELOCK;
+ KERNEL_WAKELOCK,
+ PARTIAL_WAKELOCK,
}
- /**
- * A class designed to store information related to wake locks and kernel wake locks.
- */
- public static class WakeLock extends GenericItem {
-
+ public static class WakelockInfoItem extends GenericItem {
/** Constant for JSON output */
public static final String NAME = "NAME";
/** Constant for JSON output */
- public static final String NUMBER = "NUMBER";
+ public static final String PROCESS_UID = "PROCESS_UID";
+ /** Constant for JSON output */
+ public static final String PROCESS_NAME = "PROCESS_NAME";
/** Constant for JSON output */
public static final String HELD_TIME = "HELD_TIME";
/** Constant for JSON output */
@@ -61,35 +59,35 @@ public class DumpsysBatteryInfoItem implements IItem {
public static final String CATEGORY = "CATEGORY";
private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
- NAME, NUMBER, HELD_TIME, LOCKED_COUNT, CATEGORY));
+ NAME, PROCESS_UID, PROCESS_NAME, HELD_TIME, LOCKED_COUNT, CATEGORY));
/**
- * The constructor for {@link WakeLock}
+ * The constructor for {@link WakelockItem}
*
* @param name The name of the wake lock
* @param heldTime The amount of time held in milliseconds
* @param lockedCount The number of times the wake lock was locked
* @param category The {@link WakeLockCategory} of the wake lock
*/
- public WakeLock(String name, long heldTime, int lockedCount, WakeLockCategory category) {
+ public WakelockInfoItem(String name, long heldTime, int lockedCount, WakeLockCategory category) {
this(name, null, heldTime, lockedCount, category);
}
/**
- * The constructor for {@link WakeLock}
+ * The constructor for {@link WakelockItem}
*
* @param name The name of the wake lock
- * @param number The number of the wake lock
+ * @param processUID The number of the wake lock
* @param heldTime The amount of time held in milliseconds
* @param lockedCount The number of times the wake lock was locked
* @param category The {@link WakeLockCategory} of the wake lock
*/
- public WakeLock(String name, Integer number, long heldTime, int lockedCount,
+ public WakelockInfoItem(String name, String processUID, long heldTime, int lockedCount,
WakeLockCategory category) {
super(ATTRIBUTES);
setAttribute(NAME, name);
- setAttribute(NUMBER, number);
+ setAttribute(PROCESS_UID, processUID);
setAttribute(HELD_TIME, heldTime);
setAttribute(LOCKED_COUNT, lockedCount);
setAttribute(CATEGORY, category);
@@ -103,10 +101,10 @@ public class DumpsysBatteryInfoItem implements IItem {
}
/**
- * Get the number of the wake lock.
+ * Get the process UID holding the wake lock.
*/
- public Integer getNumber() {
- return (Integer) getAttribute(NUMBER);
+ public String getProcessUID() {
+ return (String) getAttribute(PROCESS_UID);
}
/**
@@ -129,26 +127,31 @@ public class DumpsysBatteryInfoItem implements IItem {
public WakeLockCategory getCategory() {
return (WakeLockCategory) getAttribute(CATEGORY);
}
- }
- private Collection<WakeLock> mWakeLocks = new LinkedList<WakeLock>();
+ /**
+ * Set the process name holding the wake lock
+ */
+ public void setWakelockProcessName(String processName) {
+ setAttribute(PROCESS_NAME, processName);
+ }
+ }
/**
- * Add a wakelock from the battery info section.
+ * Add a wakelock from the battery stats section.
*
* @param name The name of the wake lock.
- * @param number The number of the wake lock.
+ * @param processUID The number of the wake lock.
* @param heldTime The held time of the wake lock.
* @param timesCalled The number of times the wake lock has been called.
* @param category The {@link WakeLockCategory} of the wake lock.
*/
- public void addWakeLock(String name, Integer number, long heldTime, int timesCalled,
+ public void addWakeLock(String name, String processUID, long heldTime, int timesCalled,
WakeLockCategory category) {
- mWakeLocks.add(new WakeLock(name, number, heldTime, timesCalled, category));
+ mWakeLocks.add(new WakelockInfoItem(name, processUID, heldTime, timesCalled, category));
}
/**
- * Add a wakelock from the battery info section.
+ * Add a wakelock from the battery stats section.
*
* @param name The name of the wake lock.
* @param heldTime The held time of the wake lock.
@@ -161,15 +164,15 @@ public class DumpsysBatteryInfoItem implements IItem {
}
/**
- * Get a list of {@link WakeLock} objects matching a given {@link WakeLockCategory}.
+ * Get a list of {@link WakelockInfoItem} objects matching a given {@link WakeLockCategory}.
*/
- public List<WakeLock> getWakeLocks(WakeLockCategory category) {
- LinkedList<WakeLock> wakeLocks = new LinkedList<WakeLock>();
+ public List<WakelockInfoItem> getWakeLocks(WakeLockCategory category) {
+ LinkedList<WakelockInfoItem> wakeLocks = new LinkedList<WakelockInfoItem>();
if (category == null) {
return wakeLocks;
}
- for (WakeLock wakeLock : mWakeLocks) {
+ for (WakelockInfoItem wakeLock : mWakeLocks) {
if (category.equals(wakeLock.getCategory())) {
wakeLocks.add(wakeLock);
}
@@ -178,11 +181,23 @@ public class DumpsysBatteryInfoItem implements IItem {
}
/**
+ * Get a list of {@link WakelockInfoItem} .
+ */
+ public List<WakelockInfoItem> getWakeLocks() {
+ LinkedList<WakelockInfoItem> wakeLocks = new LinkedList<WakelockInfoItem>();
+
+ for (WakelockInfoItem wakeLock : mWakeLocks) {
+ wakeLocks.add(wakeLock);
+ }
+ return wakeLocks;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
public IItem merge(IItem other) throws ConflictingItemException {
- throw new ConflictingItemException("Dumpsys battery info items cannot be merged");
+ throw new ConflictingItemException("Wakelock items cannot be merged");
}
/**
@@ -199,14 +214,16 @@ public class DumpsysBatteryInfoItem implements IItem {
@Override
public JSONObject toJson() {
JSONObject object = new JSONObject();
- try {
- JSONArray wakeLocks = new JSONArray();
- for (WakeLock wakeLock : mWakeLocks) {
- wakeLocks.put(wakeLock.toJson());
+ if (mWakeLocks != null) {
+ try {
+ JSONArray wakeLocks = new JSONArray();
+ for (WakelockInfoItem wakeLock : mWakeLocks) {
+ wakeLocks.put(wakeLock.toJson());
+ }
+ object.put(WAKELOCKS, wakeLocks);
+ } catch (JSONException e) {
+ // Ignore
}
- object.put(WAKELOCKS, wakeLocks);
- } catch (JSONException e) {
- // Ignore
}
return object;
}
diff --git a/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java b/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java
new file mode 100644
index 0000000..f1bbed0
--- /dev/null
+++ b/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 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.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.InterruptItem;
+import com.android.loganalysis.item.ProcessUsageItem;
+import com.android.loganalysis.item.WakelockItem;
+import com.android.loganalysis.util.NumberFormattingUtil;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * A {@link IParser} to parse the battery stats section of the bugreport
+ */
+public class BatteryStatsDetailedInfoParser extends AbstractSectionParser {
+
+ private static final String WAKELOCK_SECTION_REGEX = "^\\s*All kernel wake locks:$";
+ private static final String INTERRUPT_SECTION_REGEX = "^\\s*All wakeup reasons:$";
+ private static final String PROCESS_USAGE_SECTION_REGEX = "^\\s*0:$";
+
+ /**
+ * Matches: Time on battery: 7h 45m 54s 332ms (98.3%) realtime, 4h 40m 51s 315ms (59.3%) uptime
+ */
+ private static final Pattern TIME_ON_BATTERY_PATTERN = Pattern.compile(
+ "^\\s*Time on battery: (?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?" +
+ "\\s?(?:(\\d+)ms)?.*");
+ /**
+ * Matches:Time on battery screen off: 1d 4h 6m 16s 46ms (99.1%) realtime, 6h 37m 49s 201ms
+ */
+ private static final Pattern SCREEN_OFF_TIME_PATTERN = Pattern.compile("^\\s*Time on battery "
+ + "screen off: (?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?\\s?"
+ + "(?:(\\d+)ms).*");
+
+ private WakelockParser mWakelockParser = new WakelockParser();
+ private InterruptParser mInterruptParser = new InterruptParser();
+ private ProcessUsageParser mProcessUsageParser = new ProcessUsageParser();
+
+ private IParser mBatteryTimeParser = new IParser() {
+ @Override
+ public BatteryStatsDetailedInfoItem parse(List<String> lines) {
+ BatteryStatsDetailedInfoItem detailedInfo = null;
+ long timeOnBattery = 0, screenOffTime = 0;
+ Matcher m = null;
+ for (String line : lines) {
+ if (detailedInfo == null && !"".equals(line.trim())) {
+ detailedInfo = new BatteryStatsDetailedInfoItem();
+ }
+ m = TIME_ON_BATTERY_PATTERN.matcher(line);
+ if (m.matches()) {
+ timeOnBattery = NumberFormattingUtil.getMs(
+ NumberFormattingUtil.parseIntOrZero(m.group(1)),
+ NumberFormattingUtil.parseIntOrZero(m.group(2)),
+ NumberFormattingUtil.parseIntOrZero(m.group(3)),
+ NumberFormattingUtil.parseIntOrZero(m.group(4)),
+ NumberFormattingUtil.parseIntOrZero(m.group(5)));
+ detailedInfo.setTimeOnBattery(timeOnBattery);
+ } else {
+ m = SCREEN_OFF_TIME_PATTERN.matcher(line);
+ if (m.matches()) {
+ screenOffTime = NumberFormattingUtil.getMs(
+ NumberFormattingUtil.parseIntOrZero(m.group(1)),
+ NumberFormattingUtil.parseIntOrZero(m.group(2)),
+ NumberFormattingUtil.parseIntOrZero(m.group(3)),
+ NumberFormattingUtil.parseIntOrZero(m.group(4)),
+ NumberFormattingUtil.parseIntOrZero(m.group(5)));
+ detailedInfo.setScreenOnTime(getScreenOnTime(timeOnBattery, screenOffTime));
+ return detailedInfo;
+ }
+ }
+ }
+ return detailedInfo;
+ }
+
+ private long getScreenOnTime(long timeOnBattery, long screenOffTime) {
+ if (timeOnBattery > screenOffTime) {
+ return (timeOnBattery - screenOffTime);
+ }
+ return 0;
+ }
+ };
+
+ private BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfoItem = null;
+ private boolean mParsedInput = false;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The {@link BatteryStatsDetailedInfoItem}
+ */
+ @Override
+ public BatteryStatsDetailedInfoItem parse(List<String> lines) {
+ setup();
+ for (String line : lines) {
+ if (!mParsedInput && !"".equals(line.trim())) {
+ mParsedInput = true;
+ }
+ parseLine(line);
+ }
+ commit();
+ return mBatteryStatsDetailedInfoItem;
+ }
+
+ /**
+ * Sets up the parser by adding the section parsers.
+ */
+ protected void setup() {
+ setParser(mBatteryTimeParser);
+ addSectionParser(mWakelockParser, WAKELOCK_SECTION_REGEX);
+ addSectionParser(mInterruptParser, INTERRUPT_SECTION_REGEX);
+ addSectionParser(mProcessUsageParser, PROCESS_USAGE_SECTION_REGEX);
+ }
+
+ /**
+ * Set the {@link BatteryStatsDetailedInfoItem}
+ *
+ */
+ @Override
+ protected void onSwitchParser() {
+ if (mBatteryStatsDetailedInfoItem == null) {
+ mBatteryStatsDetailedInfoItem = (BatteryStatsDetailedInfoItem)
+ getSection(mBatteryTimeParser);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void commit() {
+ // signal EOF
+ super.commit();
+ if (mParsedInput) {
+ if (mBatteryStatsDetailedInfoItem == null) {
+ mBatteryStatsDetailedInfoItem = new BatteryStatsDetailedInfoItem();
+ }
+ }
+
+ if (mBatteryStatsDetailedInfoItem != null) {
+ mBatteryStatsDetailedInfoItem.setWakelockItem(
+ (WakelockItem) getSection(mWakelockParser));
+ mBatteryStatsDetailedInfoItem.setInterruptItem(
+ (InterruptItem) getSection(mInterruptParser));
+ mBatteryStatsDetailedInfoItem.setProcessUsageItem(
+ (ProcessUsageItem) getSection(mProcessUsageParser));
+ }
+ }
+}
diff --git a/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java b/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java
new file mode 100644
index 0000000..d5177e3
--- /dev/null
+++ b/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 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.BatteryDischargeItem;
+import com.android.loganalysis.item.BatteryDischargeItem.BatteryDischargeInfoItem;
+import com.android.loganalysis.item.BatteryStatsSummaryInfoItem;
+import com.android.loganalysis.util.NumberFormattingUtil;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to parse batterystats summary
+ */
+public class BatteryStatsSummaryInfoParser implements IParser{
+
+ /**
+ * Matches: 0 (15) RESET:TIME: 2015-01-18-12-56-57
+ */
+ private static final Pattern RESET_TIME_PATTERN = Pattern.compile("^\\s*"
+ + "\\d\\s*\\(\\d+\\)\\s*RESET:TIME:\\s*(\\d+)-(\\d+)-(\\d+)-(\\d+)-(\\d+)-(\\d+)$");
+
+ /**
+ * Matches: +1d01h03m37s246ms (1) 028 c10400010 -running -wake_lock
+ */
+ private static final Pattern BATTERY_DISCHARGE_PATTERN = Pattern.compile(
+ "^\\s*\\+(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?(?:(\\d+)ms)? \\(\\d+\\) "
+ + "(\\d+) \\w+ .*");
+
+ private BatteryDischargeItem mBatteryDischarge = new BatteryDischargeItem();
+ private BatteryStatsSummaryInfoItem mItem = new BatteryStatsSummaryInfoItem();
+ private long mBatteryDischargeRateAvg = 0;
+ private int mBatteryDischargeSamples = 0;
+ private Calendar mResetTime;
+ private static final int BATTERY_GROUP_LIMIT = 10;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The {@link BatteryStatsSummaryInfoItem}.
+ */
+ @Override
+ public BatteryStatsSummaryInfoItem parse(List<String> lines) {
+ Matcher resetTimeMatcher = null;
+ Matcher dischargeMatcher = null;
+
+ long previousDischargeElapsedTime= 0;
+ int previousBatteryLevel = 0;
+ boolean batteryDischargedFully = false;
+ for (String line : lines) {
+ resetTimeMatcher = RESET_TIME_PATTERN.matcher(line);
+ dischargeMatcher = BATTERY_DISCHARGE_PATTERN.matcher(line);
+ if (resetTimeMatcher.matches()) {
+ mResetTime = new GregorianCalendar();
+ final int year = Integer.parseInt(resetTimeMatcher.group(1));
+ final int month = Integer.parseInt(resetTimeMatcher.group(2));
+ final int day = Integer.parseInt(resetTimeMatcher.group(3));
+ final int hour = Integer.parseInt(resetTimeMatcher.group(4));
+ final int minute = Integer.parseInt(resetTimeMatcher.group(5));
+ final int second = Integer.parseInt(resetTimeMatcher.group(6));
+ // Calendar month is zero indexed but the parsed date is 1-12
+ mResetTime.set(year, (month - 1), day, hour, minute, second);
+ } else if (dischargeMatcher.matches()) {
+ final int days = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(1));
+ final int hours = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(2));
+ final int mins = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(3));
+ final int secs = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(4));
+ final int msecs = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(5));
+ final int batteryLevel = Integer.parseInt(dischargeMatcher.group(6));
+ if (batteryLevel == 0) {
+ // Ignore the subsequent battery drop readings
+ batteryDischargedFully = true;
+ continue;
+ } else if (previousBatteryLevel == 0) {
+ // Ignore the first drop
+ previousBatteryLevel = batteryLevel;
+ continue;
+ } else if (!batteryDischargedFully && previousBatteryLevel != batteryLevel) {
+ long elapsedTime = NumberFormattingUtil.getMs(days, hours, mins, secs, msecs);
+ mBatteryDischargeRateAvg += (elapsedTime - previousDischargeElapsedTime);
+ mBatteryDischargeSamples++;
+ mBatteryDischarge.addBatteryDischargeInfo(
+ getDischargeClockTime(days, hours, mins, secs),
+ (elapsedTime - previousDischargeElapsedTime), batteryLevel);
+ previousDischargeElapsedTime = elapsedTime;
+ previousBatteryLevel = batteryLevel;
+ }
+ }
+ }
+ mItem.setBatteryDischargeRate(getAverageDischargeRate());
+ mItem.setPeakDischargeTime(getPeakDischargeTime());
+ return mItem;
+ }
+
+ private Calendar getDischargeClockTime(int days, int hours, int mins, int secs) {
+ Calendar dischargeClockTime = new GregorianCalendar();
+
+ dischargeClockTime.setTime(mResetTime.getTime());
+ dischargeClockTime.add(Calendar.DATE, days);
+ dischargeClockTime.add(Calendar.HOUR, hours);
+ dischargeClockTime.add(Calendar.MINUTE, mins);
+ dischargeClockTime.add(Calendar.SECOND, secs);
+ return dischargeClockTime;
+ }
+
+ private String getAverageDischargeRate() {
+ if (mBatteryDischargeSamples == 0) {
+ return "The battery did not discharge";
+ }
+
+ final long minsPerLevel = mBatteryDischargeRateAvg / (mBatteryDischargeSamples * 60 * 1000);
+ return String.format("The battery dropped a level %d mins on average", minsPerLevel);
+ }
+
+ private String getPeakDischargeTime() {
+
+ int peakDischargeStartBatteryLevel = 0, peakDischargeStopBatteryLevel = 0;
+ long minDischargeDuration = 0;
+ Calendar peakDischargeStartTime= null, peakDischargeStopTime = null;
+ Queue <BatteryDischargeInfoItem> batteryDischargeWindow =
+ new LinkedList <BatteryDischargeInfoItem>();
+ long sumDischargeDuration = 0;
+ for (BatteryDischargeInfoItem dischargeSteps : mBatteryDischarge.getDischargeStepsInfo()) {
+ batteryDischargeWindow.add(dischargeSteps);
+ sumDischargeDuration += dischargeSteps.getElapsedTime();
+ if (batteryDischargeWindow.size() >= BATTERY_GROUP_LIMIT) {
+ final long averageDischargeDuration = sumDischargeDuration/BATTERY_GROUP_LIMIT;
+ final BatteryDischargeInfoItem startNode = batteryDischargeWindow.remove();
+ sumDischargeDuration -= startNode.getElapsedTime();
+
+ if (minDischargeDuration == 0 || averageDischargeDuration < minDischargeDuration) {
+ minDischargeDuration = averageDischargeDuration;
+ peakDischargeStartBatteryLevel = startNode.getBatteryLevel();
+ peakDischargeStopBatteryLevel = dischargeSteps.getBatteryLevel();
+ peakDischargeStartTime = startNode.getClockTime();
+ peakDischargeStopTime = dischargeSteps.getClockTime();
+ }
+ }
+ }
+ if (peakDischargeStartTime != null && peakDischargeStopTime != null &&
+ peakDischargeStartBatteryLevel > 0 && peakDischargeStopBatteryLevel > 0) {
+ return String.format(
+ "The peak discharge time was during %s to %s where battery dropped from %d to "
+ + "%d", peakDischargeStartTime.getTime().toString(),
+ peakDischargeStopTime.getTime().toString(), peakDischargeStartBatteryLevel,
+ peakDischargeStopBatteryLevel);
+
+ } else {
+ return "The battery did not discharge";
+ }
+ }
+
+ /**
+ * Get the {@link BatteryStatsSummaryInfoItem}.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ BatteryStatsSummaryInfoItem getItem() {
+ return mItem;
+ }
+}
diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
deleted file mode 100644
index afd4359..0000000
--- a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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);
- }
-}
diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java
new file mode 100644
index 0000000..3e8be2c
--- /dev/null
+++ b/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
+import com.android.loganalysis.item.BatteryStatsSummaryInfoItem;
+
+import java.util.List;
+
+
+/**
+ * A {@link IParser} to parse the battery stats section of the bugreport
+ */
+public class DumpsysBatteryStatsParser extends AbstractSectionParser {
+
+ private static final String SUMMARY_INFO_SECTION_REGEX =
+ "Battery History \\(\\d+% used, \\d+KB used of \\d+KB, \\d+ strings using \\d+KB\\):$";
+ private static final String DETAILED_INFO_SECTION_REGEX = "^Statistics since last charge:$";
+ private static final String NOOP_SECTION_REGEX = "^Statistics since last unplugged:$";
+
+ private BatteryStatsSummaryInfoParser mSummaryParser = new BatteryStatsSummaryInfoParser();
+ private BatteryStatsDetailedInfoParser mDetailedParser = new BatteryStatsDetailedInfoParser();
+
+ private DumpsysBatteryStatsItem mDumpsysBatteryStatsItem = null;
+ private boolean mParsedInput = false;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The {@link DumpsysBatteryStatsItem}
+ */
+ @Override
+ public DumpsysBatteryStatsItem parse(List<String> lines) {
+ setup();
+ for (String line : lines) {
+ if (!mParsedInput && !"".equals(line.trim())) {
+ mParsedInput = true;
+ }
+ parseLine(line);
+ }
+ commit();
+ return mDumpsysBatteryStatsItem;
+ }
+
+ /**
+ * Sets up the parser by adding the section parsers.
+ */
+ protected void setup() {
+ addSectionParser(mSummaryParser, SUMMARY_INFO_SECTION_REGEX);
+ addSectionParser(mDetailedParser, DETAILED_INFO_SECTION_REGEX);
+ addSectionParser(new NoopParser(), NOOP_SECTION_REGEX);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void commit() {
+ // signal EOF
+ super.commit();
+ if (mParsedInput) {
+ if (mDumpsysBatteryStatsItem == null) {
+ mDumpsysBatteryStatsItem = new DumpsysBatteryStatsItem();
+ }
+ }
+
+ if (mDumpsysBatteryStatsItem != null) {
+ mDumpsysBatteryStatsItem.setBatteryStatsSummarytem(
+ (BatteryStatsSummaryInfoItem) getSection(mSummaryParser));
+ mDumpsysBatteryStatsItem.setDetailedBatteryStatsItem(
+ (BatteryStatsDetailedInfoItem) getSection(mDetailedParser));
+ }
+ }
+}
diff --git a/src/com/android/loganalysis/parser/DumpsysParser.java b/src/com/android/loganalysis/parser/DumpsysParser.java
index e950341..d8941e0 100644
--- a/src/com/android/loganalysis/parser/DumpsysParser.java
+++ b/src/com/android/loganalysis/parser/DumpsysParser.java
@@ -15,8 +15,10 @@
*/
package com.android.loganalysis.parser;
-import com.android.loganalysis.item.DumpsysBatteryInfoItem;
+
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.DumpsysProcStatsItem;
import java.util.List;
@@ -24,10 +26,14 @@ import java.util.List;
* A {@link IParser} to handle the output of the dumpsys section of the bugreport.
*/
public class DumpsysParser extends AbstractSectionParser {
- private static final String BATTERY_INFO_SECTION_REGEX = "DUMP OF SERVICE batteryinfo:";
+
+ private static final String BATTERY_STATS_SECTION_REGEX = "^DUMP OF SERVICE batterystats:$";
+ private static final String PROC_STATS_SECTION_REGEX = "^DUMP OF SERVICE procstats:";
private static final String NOOP_SECTION_REGEX = "DUMP OF SERVICE .*";
- private DumpsysBatteryInfoParser mBatteryInfoParser = new DumpsysBatteryInfoParser();
+ private DumpsysBatteryStatsParser mBatteryStatsParser = new DumpsysBatteryStatsParser();
+ private DumpsysProcStatsParser mProcStatsParser = new DumpsysProcStatsParser();
+
private DumpsysItem mDumpsys = null;
/**
@@ -53,7 +59,8 @@ public class DumpsysParser extends AbstractSectionParser {
* Sets up the parser by adding the section parsers.
*/
protected void setup() {
- addSectionParser(mBatteryInfoParser, BATTERY_INFO_SECTION_REGEX);
+ addSectionParser(mBatteryStatsParser, BATTERY_STATS_SECTION_REGEX);
+ addSectionParser(mProcStatsParser, PROC_STATS_SECTION_REGEX);
addSectionParser(new NoopParser(), NOOP_SECTION_REGEX);
}
@@ -64,10 +71,12 @@ public class DumpsysParser extends AbstractSectionParser {
protected void commit() {
// signal EOF
super.commit();
-
+ if (mDumpsys == null) {
+ mDumpsys = new DumpsysItem();
+ }
if (mDumpsys != null) {
- mDumpsys.setBatteryInfo(
- (DumpsysBatteryInfoItem) getSection(mBatteryInfoParser));
+ mDumpsys.setBatteryInfo((DumpsysBatteryStatsItem) getSection(mBatteryStatsParser));
+ mDumpsys.setProcStats((DumpsysProcStatsItem) getSection(mProcStatsParser));
}
}
}
diff --git a/src/com/android/loganalysis/parser/DumpsysProcStatsParser.java b/src/com/android/loganalysis/parser/DumpsysProcStatsParser.java
new file mode 100644
index 0000000..010036b
--- /dev/null
+++ b/src/com/android/loganalysis/parser/DumpsysProcStatsParser.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 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.DumpsysProcStatsItem;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to parse procstats and create a mapping table of process names and UIDs
+ */
+public class DumpsysProcStatsParser implements IParser {
+
+ /**
+ * Matches: * com.google.android.googlequicksearchbox:search / u0a19 / v300401240: -----
+ */
+ private static final Pattern UID = Pattern.compile("^\\s*\\* (.*):?.*/ (.*)/.*");
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The {@link DumpsysProcStatsItem}.
+ */
+ @Override
+ public DumpsysProcStatsItem parse(List<String> lines) {
+ DumpsysProcStatsItem item = new DumpsysProcStatsItem();
+ for (String line : lines) {
+ Matcher m = UID.matcher(line);
+ if(m.matches()) {
+ item.put(m.group(2).trim(), m.group(1).trim());
+ }
+ }
+ return item;
+ }
+
+}
diff --git a/src/com/android/loganalysis/parser/InterruptParser.java b/src/com/android/loganalysis/parser/InterruptParser.java
new file mode 100644
index 0000000..26417c4
--- /dev/null
+++ b/src/com/android/loganalysis/parser/InterruptParser.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.InterruptItem;
+import com.android.loganalysis.item.InterruptItem.InterruptCategory;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to parse wake up interrupts
+ */
+public class InterruptParser implements IParser {
+
+ /**
+ * Matches: Wakeup reason 289:bcmsdh_sdmmc:200:qcom,smd-rpm:240:msmgpio:
+ * 20m 5s 194ms (1485 times) realtime
+ */
+ private static final Pattern Interrupt = Pattern.compile(
+ "^\\s*Wakeup reason (.*): (?:\\d+h )?(?:\\d+m )?(?:\\d+s )(?:\\d+ms )" +
+ "\\((\\d+) times\\) realtime");
+
+ private InterruptItem mItem = new InterruptItem();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The {@link InterruptItem}.
+ */
+ @Override
+ public InterruptItem parse(List<String> lines) {
+ for (String line : lines) {
+ Matcher m = Interrupt.matcher(line);
+ if(m.matches()) {
+ final String interruptName = m.group(1);
+ final int interruptCount = Integer.parseInt(m.group(2));
+ mItem.addInterrupt(interruptName, interruptCount,
+ getInterruptCategory(interruptName));
+ } else {
+ // Done with interrupts
+ break;
+ }
+ }
+ return mItem;
+ }
+
+ /**
+ * Get the {@link InterruptItem}.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ InterruptItem getItem() {
+ return mItem;
+ }
+
+ private InterruptCategory getInterruptCategory(String interruptName) {
+ if (interruptName.contains("bcmsdh_sdmmc")) {
+ return InterruptCategory.WIFI_INTERRUPT;
+ } else if (interruptName.contains("smd-modem") ||
+ interruptName.contains("smsm-modem")) {
+ return InterruptCategory.MODEM_INTERRUPT;
+ } else if (interruptName.contains("smd-adsp")) {
+ return InterruptCategory.ADSP_INTERRUPT;
+ } else if (interruptName.contains("max77686-irq") ||
+ interruptName.contains("cpcap-irq") ||
+ interruptName.contains("TWL6030-PIH")) {
+ return InterruptCategory.ALARM_INTERRUPT;
+ }
+
+ return InterruptCategory.UNKNOWN_INTERRUPT;
+ }
+
+}
diff --git a/src/com/android/loganalysis/parser/ProcessUsageParser.java b/src/com/android/loganalysis/parser/ProcessUsageParser.java
new file mode 100644
index 0000000..9609cd2
--- /dev/null
+++ b/src/com/android/loganalysis/parser/ProcessUsageParser.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 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.ProcessUsageItem;
+import com.android.loganalysis.item.ProcessUsageItem.SensorInfoItem;
+import com.android.loganalysis.util.NumberFormattingUtil;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to handle the parsing of process usage information
+ */
+public class ProcessUsageParser implements IParser {
+
+ private ProcessUsageItem mItem = new ProcessUsageItem();
+ private LinkedList<SensorInfoItem> mSensorUsage = new LinkedList<SensorInfoItem>();
+
+ /**
+ * Matches: 1000:
+ */
+ private static final Pattern UID_PATTERN = Pattern.compile("^\\s*(\\w+):$");
+
+ /**
+ * Matches: Sensor 1: 12m 52s 311ms realtime (29 times)
+ */
+ private static final Pattern SENSOR_PATTERN = Pattern.compile(
+ "^\\s*Sensor (\\d+): (?:(\\d+)d\\s)?"
+ + "(?:(\\d+)h\\s)?(?:(\\d+)m\\s)?(?:(\\d+)s\\s)?(\\d+)ms "
+ + "realtime \\((\\d+) times\\)$");
+
+ /**
+ * Matches: 507 wakeup alarms
+ */
+ private static final Pattern ALARM_PATTERN = Pattern.compile("^\\s*(\\d+) wakeup alarms$");
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ProcessUsageItem parse(List<String> lines) {
+ String processUid = null;
+ int alarmWakeups = 0;
+ for (String line : lines) {
+ Matcher m = UID_PATTERN.matcher(line);
+ if (m.matches()) {
+ if (processUid != null) {
+ // Save the process usage info for the previous process
+ mItem.addProcessUsage(processUid, alarmWakeups, mSensorUsage);
+ }
+ processUid = m.group(1);
+ mSensorUsage = new LinkedList<SensorInfoItem>();
+ continue;
+ }
+ m = SENSOR_PATTERN.matcher(line);
+ if (m.matches()) {
+ final long duration = NumberFormattingUtil.getMs(
+ NumberFormattingUtil.parseIntOrZero(m.group(2)),
+ NumberFormattingUtil.parseIntOrZero(m.group(3)),
+ NumberFormattingUtil.parseIntOrZero(m.group(4)),
+ NumberFormattingUtil.parseIntOrZero(m.group(5)),
+ NumberFormattingUtil.parseIntOrZero(m.group(6)));
+ mSensorUsage.add(new SensorInfoItem(m.group(1), duration));
+ continue;
+ }
+ m = ALARM_PATTERN.matcher(line);
+ if (m.matches()) {
+ alarmWakeups = Integer.parseInt(m.group(1));
+ }
+ }
+ return mItem;
+ }
+
+ /**
+ * Get the {@link ProcessUsageItem}.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ ProcessUsageItem getItem() {
+ return mItem;
+ }
+}
diff --git a/src/com/android/loganalysis/parser/WakelockParser.java b/src/com/android/loganalysis/parser/WakelockParser.java
new file mode 100644
index 0000000..91652f7
--- /dev/null
+++ b/src/com/android/loganalysis/parser/WakelockParser.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 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.WakelockItem;
+import com.android.loganalysis.item.WakelockItem.WakeLockCategory;
+import com.android.loganalysis.util.NumberFormattingUtil;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to handle the parsing of wakelock information
+ */
+public class WakelockParser implements IParser {
+
+ private static final Pattern PARTIAL_WAKE_LOCK_START_PAT = Pattern.compile(
+ "^\\s*All partial wake locks:$");
+
+ private static final String WAKE_LOCK_PAT_SUFFIX =
+ "(?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?\\s?(?:(\\d+)ms)?"
+ + "\\s?\\((\\d+) times\\) realtime";
+
+ /**
+ * Match a valid line such as:
+ * " Kernel Wake lock PowerManagerService.WakeLocks: 1h 13m 50s 950ms (2858 times) realtime"
+ */
+ private static final Pattern KERNEL_WAKE_LOCK_PAT = Pattern.compile(
+ "^\\s*Kernel Wake lock (.+): " + WAKE_LOCK_PAT_SUFFIX);
+ /**
+ * Match a valid line such as:
+ * " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime";
+ */
+ private static final Pattern PARTIAL_WAKE_LOCK_PAT = Pattern.compile(
+ "^\\s*Wake lock (.*)\\s(.+): " + WAKE_LOCK_PAT_SUFFIX);
+
+ private WakelockItem mItem = new WakelockItem();
+
+ public static final int TOP_WAKELOCK_COUNT = 5;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public WakelockItem parse(List<String> lines) {
+ boolean inPartialWakeLock = false;
+ Matcher m = null;
+ int wakelockCounter = 0;
+ for (String line : lines) {
+ if ("".equals(line.trim())) {
+ if (inPartialWakeLock) {
+ // Done with parsing wakelock sections
+ break;
+ } else {
+ // Done with parsing kernel wakelocks and continue with
+ // partial wakelock
+ wakelockCounter = 0;
+ continue;
+ }
+ }
+ m = KERNEL_WAKE_LOCK_PAT.matcher(line);
+ if (m.matches()) {
+ if (wakelockCounter < TOP_WAKELOCK_COUNT &&
+ !line.contains("PowerManagerService.WakeLocks")) {
+ parseKernelWakeLock(line, WakeLockCategory.KERNEL_WAKELOCK);
+ wakelockCounter++;
+ }
+ continue;
+ }
+ m = PARTIAL_WAKE_LOCK_START_PAT.matcher(line);
+ if (m.matches()) {
+ inPartialWakeLock = true;
+ continue;
+ }
+ m = PARTIAL_WAKE_LOCK_PAT.matcher(line);
+ if (m.matches() && wakelockCounter < TOP_WAKELOCK_COUNT) {
+ parsePartialWakeLock(line, WakeLockCategory.PARTIAL_WAKELOCK);
+ wakelockCounter++;
+ }
+ }
+ return mItem;
+ }
+
+ /**
+ * Parse a line of output and add it to wakelock 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 wakelockTime = NumberFormattingUtil.getMs(
+ NumberFormattingUtil.parseIntOrZero(m.group(2)),
+ NumberFormattingUtil.parseIntOrZero(m.group(3)),
+ NumberFormattingUtil.parseIntOrZero(m.group(4)),
+ NumberFormattingUtil.parseIntOrZero(m.group(5)),
+ NumberFormattingUtil.parseIntOrZero(m.group(6)));
+
+ final int timesCalled = Integer.parseInt(m.group(7));
+
+ mItem.addWakeLock(name, wakelockTime, timesCalled, category);
+ }
+
+ /**
+ * Parse a line of output and add it to wake lock section.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ void parsePartialWakeLock(String line, WakeLockCategory category) {
+ Matcher m = PARTIAL_WAKE_LOCK_PAT.matcher(line);
+ if (!m.matches()) {
+ return;
+ }
+ final String processUID = m.group(1);
+ final String name = m.group(2);
+ final long wakelockTime = NumberFormattingUtil.getMs(
+ NumberFormattingUtil.parseIntOrZero(m.group(3)),
+ NumberFormattingUtil.parseIntOrZero(m.group(4)),
+ NumberFormattingUtil.parseIntOrZero(m.group(5)),
+ NumberFormattingUtil.parseIntOrZero(m.group(6)),
+ NumberFormattingUtil.parseIntOrZero(m.group(7)));
+ final int timesCalled = Integer.parseInt(m.group(8));
+
+ mItem.addWakeLock(name, processUID, wakelockTime, timesCalled, category);
+ }
+
+ /**
+ * Get the {@link WakelockItem}.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ WakelockItem getItem() {
+ return mItem;
+ }
+}
diff --git a/src/com/android/loganalysis/rule/AbstractPowerRule.java b/src/com/android/loganalysis/rule/AbstractPowerRule.java
new file mode 100644
index 0000000..cab46a3
--- /dev/null
+++ b/src/com/android/loganalysis/rule/AbstractPowerRule.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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.rule;
+
+import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.BatteryStatsSummaryInfoItem;
+import com.android.loganalysis.item.DumpsysProcStatsItem;
+
+import org.json.JSONObject;
+
+/**
+ * Base class for all power rules
+ */
+public abstract class AbstractPowerRule implements IRule {
+
+ private BugreportItem mBugreportItem;
+ private BatteryStatsSummaryInfoItem mPowerSummaryAnalysisItem;
+ private BatteryStatsDetailedInfoItem mPowerDetailedAnalysisItem;
+ private DumpsysProcStatsItem mProcStatsItem;
+
+ public AbstractPowerRule(BugreportItem bugreportItem) {
+ mBugreportItem = bugreportItem;
+ mPowerSummaryAnalysisItem = mBugreportItem.getDumpsys().getBatteryStats().
+ getBatteryStatsSummaryItem();
+ mPowerDetailedAnalysisItem = mBugreportItem.getDumpsys().getBatteryStats().
+ getDetailedBatteryStatsItem();
+ mProcStatsItem = mBugreportItem.getDumpsys().getProcStats();
+ }
+
+ protected long getTimeOnBattery() {
+ return mPowerDetailedAnalysisItem.getTimeOnBattery();
+ }
+
+ protected BatteryStatsSummaryInfoItem getSummaryItem() {
+ return mPowerSummaryAnalysisItem;
+ }
+
+ protected BatteryStatsDetailedInfoItem getDetailedAnalysisItem() {
+ return mPowerDetailedAnalysisItem;
+ }
+
+ protected DumpsysProcStatsItem getProcStatsItem() {
+ return mProcStatsItem;
+ }
+
+ @Override
+ public abstract void applyRule();
+
+ @Override
+ public abstract JSONObject getAnalysis();
+
+}
diff --git a/src/com/android/loganalysis/rule/IRule.java b/src/com/android/loganalysis/rule/IRule.java
new file mode 100644
index 0000000..416d7f6
--- /dev/null
+++ b/src/com/android/loganalysis/rule/IRule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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.rule;
+
+import org.json.JSONObject;
+
+/**
+ * An interface which defines the rules. Individual rules will apply the filter on the parsed data
+ * and return the high level analysis in JSON Format
+ */
+public interface IRule {
+ // Apply the rules
+ public void applyRule();
+
+ public JSONObject getAnalysis();
+}
diff --git a/src/com/android/loganalysis/rule/ProcessUsageRule.java b/src/com/android/loganalysis/rule/ProcessUsageRule.java
new file mode 100644
index 0000000..4e15aee
--- /dev/null
+++ b/src/com/android/loganalysis/rule/ProcessUsageRule.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 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.rule;
+
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysProcStatsItem;
+import com.android.loganalysis.item.ProcessUsageItem;
+import com.android.loganalysis.item.ProcessUsageItem.ProcessUsageInfoItem;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+
+/**
+ * Rules definition for Process usage
+ */
+public class ProcessUsageRule extends AbstractPowerRule {
+
+ private static final String PROCESS_USAGE_ANALYSIS = "PROCESS_USAGE_ANALYSIS";
+ private static final long ALARM_THRESHOLD = 60000;
+
+ private StringBuffer mAnalysisBuffer;
+
+ public ProcessUsageRule (BugreportItem bugreportItem) {
+ super(bugreportItem);
+ }
+
+
+ @Override
+ public void applyRule() {
+ mAnalysisBuffer = new StringBuffer();
+ ProcessUsageItem processUsageItem = getDetailedAnalysisItem().getProcessUsageItem();
+ DumpsysProcStatsItem procStatsItem = getProcStatsItem();
+ if (processUsageItem != null && procStatsItem!= null) {
+ for (ProcessUsageInfoItem usage : processUsageItem.getProcessUsage()) {
+ if (usage.getAlarmWakeups() > 0) {
+ final long alarmsPerMs = getTimeOnBattery()/usage.getAlarmWakeups();
+ if (alarmsPerMs < ALARM_THRESHOLD) {
+ final String processName = procStatsItem.get(usage.getProcessUID());
+ if (processName != null) {
+ mAnalysisBuffer.append(processName);
+ } else {
+ mAnalysisBuffer.append(usage.getProcessUID());
+ }
+ mAnalysisBuffer.append(" has requested frequent repeating alarms");
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public JSONObject getAnalysis() {
+ JSONObject usageAnalysis = new JSONObject();
+ try {
+ usageAnalysis.put(PROCESS_USAGE_ANALYSIS, mAnalysisBuffer.toString());
+ } catch (JSONException e) {
+ // do nothing
+ }
+ return usageAnalysis;
+ }
+}
diff --git a/src/com/android/loganalysis/rule/RuleEngine.java b/src/com/android/loganalysis/rule/RuleEngine.java
new file mode 100644
index 0000000..add61e6
--- /dev/null
+++ b/src/com/android/loganalysis/rule/RuleEngine.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.rule;
+
+import com.android.loganalysis.item.BugreportItem;
+
+import org.json.JSONArray;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+
+/**
+ * Applies rules to the parsed bugreport
+ */
+public class RuleEngine {
+
+ public enum RuleType{
+ ALL, POWER;
+ }
+
+ BugreportItem mBugreportItem;
+ private Collection<IRule> mRulesList;
+
+ public RuleEngine(BugreportItem bugreportItem) {
+ mBugreportItem = bugreportItem;
+ mRulesList = new LinkedList<IRule>();
+ }
+
+ public void registerRules(RuleType ruleType) {
+ if (ruleType == RuleType.ALL) {
+ // add all rules
+ addPowerRules();
+ } else if (ruleType == RuleType.POWER) {
+ addPowerRules();
+ }
+ }
+
+ public void executeRules() {
+ for (IRule rule : mRulesList) {
+ rule.applyRule();
+ }
+ }
+
+ public JSONArray getAnalysis() {
+ JSONArray result = new JSONArray();
+ for (IRule rule : mRulesList) {
+ result.put(rule.getAnalysis());
+ }
+ return result;
+ }
+
+ private void addPowerRules() {
+ mRulesList.add(new WakelockRule(mBugreportItem));
+ mRulesList.add(new ProcessUsageRule(mBugreportItem));
+ }
+}
diff --git a/src/com/android/loganalysis/rule/WakelockRule.java b/src/com/android/loganalysis/rule/WakelockRule.java
new file mode 100644
index 0000000..b2db2cd
--- /dev/null
+++ b/src/com/android/loganalysis/rule/WakelockRule.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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.rule;
+
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.WakelockItem;
+import com.android.loganalysis.item.WakelockItem.WakelockInfoItem;
+import com.android.loganalysis.util.NumberFormattingUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Rules definition for wakelock
+ */
+public class WakelockRule extends AbstractPowerRule {
+
+ private static final String WAKELOCK_ANALYSIS = "WAKELOCK_ANALYSIS";
+ private static final float WAKELOCK_HELD_TIME_THRESHOLD_PERCENTAGE = 0.1f; // 10%
+
+ private String mAnalysis = null;
+
+ public WakelockRule (BugreportItem bugreportItem) {
+ super(bugreportItem);
+ }
+
+ @SuppressWarnings("cast")
+ @Override
+ public void applyRule() {
+ WakelockItem wakelockItem = getDetailedAnalysisItem().getWakelockItem();
+ if (wakelockItem != null) {
+ long wakelockThreshold = (long)(getTimeOnBattery()
+ * WAKELOCK_HELD_TIME_THRESHOLD_PERCENTAGE);
+
+ for (WakelockInfoItem wakelocks : wakelockItem.getWakeLocks()) {
+ if (wakelocks.getHeldTime() > wakelockThreshold) {
+ mAnalysis = String.format("%s %s is held for %s", wakelocks.getName(),
+ wakelocks.getCategory(),
+ NumberFormattingUtil.getDuration(wakelocks.getHeldTime()));
+ }
+ }
+ }
+ }
+
+ @Override
+ public JSONObject getAnalysis() {
+ JSONObject wakelockAnalysis = new JSONObject();
+ try {
+ if (mAnalysis != null) {
+ wakelockAnalysis.put(WAKELOCK_ANALYSIS, mAnalysis);
+ }
+ } catch (JSONException e) {
+ // do nothing
+ }
+ return wakelockAnalysis;
+ }
+}
diff --git a/src/com/android/loganalysis/util/NumberFormattingUtil.java b/src/com/android/loganalysis/util/NumberFormattingUtil.java
new file mode 100644
index 0000000..8652c2e
--- /dev/null
+++ b/src/com/android/loganalysis/util/NumberFormattingUtil.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import java.util.concurrent.TimeUnit;
+
+
+
+/**
+ * Utility methods for number formatting
+ */
+public class NumberFormattingUtil {
+
+ private NumberFormattingUtil() {
+ }
+
+ /**
+ * Convert days/hours/mins/secs/msecs into milliseconds.
+ */
+ public static long getMs(long days, long hours, long mins, long secs, long msecs) {
+ return (((24 * days + hours) * 60 + mins) * 60 + secs) * 1000 + msecs;
+ }
+
+ /**
+ * Convert hours/mins/secs/msecs into milliseconds.
+ */
+ public static long getMs(long hours, long mins, long secs, long msecs) {
+ return getMs(0, hours, mins, secs, 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.
+ */
+ public static long parseLongOrZero(String s) throws NumberFormatException {
+ if (s == null) {
+ return 0;
+ }
+ return Long.parseLong(s);
+ }
+
+ /**
+ * Parses a string into a int, or returns 0 if the string is null.
+ *
+ * @param s a {@link String} containing the int representation to be parsed
+ * @return the int 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.
+ */
+ public static int parseIntOrZero(String s) throws NumberFormatException {
+ if (s == null) {
+ return 0;
+ }
+ return Integer.parseInt(s);
+ }
+
+ /**
+ * Converts milliseconds to days/hours/mins/secs
+ *
+ * @param ms elapsed time in ms
+ * @return the duration in days/hours/mins/secs
+ */
+ public static String getDuration(long ms) {
+ if (ms <= 0) {
+ return "Not a valid time";
+ }
+ final long days = TimeUnit.MILLISECONDS.toDays(ms);
+ final long hrs = TimeUnit.MILLISECONDS.toHours(ms - TimeUnit.DAYS.toMillis(days));
+ final long mins = TimeUnit.MILLISECONDS.toMinutes(ms - TimeUnit.DAYS.toMillis(days)
+ - TimeUnit.HOURS.toMillis(hrs));
+ final long secs = TimeUnit.MILLISECONDS.toSeconds(ms - TimeUnit.DAYS.toMillis(days)
+ - TimeUnit.HOURS.toMillis(hrs) - TimeUnit.MINUTES.toMillis(mins));
+
+ return String.format("%dd %dh %dm %ds", days, hrs, mins, secs);
+ }
+}
+