Merge "Battery drain investigator"
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.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.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 @@
         JSON;
     }
 
+    private enum ResultType {
+        RAW, ANALYSIS;
+    }
+
     @Option(name="bugreport", description="The path to the bugreport")
     private String mBugreportPath = null;
 
@@ -67,6 +78,17 @@
     @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 @@
      */
     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);
+            }
         }
-        // TODO: Print bugreport in human readable form.
+    }
+
+    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());
+        }
     }
 
     /**
@@ -180,7 +254,18 @@
      */
     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 @@
 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 DumpsysBatteryInfoItem getBatteryInfo() {
-        return (DumpsysBatteryInfoItem) getAttribute(BATTERY_INFO);
+    public void setBatteryInfo(DumpsysBatteryStatsItem batteryStats) {
+        setAttribute(BATTERY_STATS, batteryStats);
     }
 
     /**
-     * Set the battery info section of the dumpsys.
+     * Set the {@link DumpsysProcStatsItem} of the bugreport.
      */
-    public void setBatteryInfo(DumpsysBatteryInfoItem batteryInfo) {
-        setAttribute(BATTERY_INFO, batteryInfo);
+    public void setProcStats(DumpsysProcStatsItem procStats) {
+        setAttribute(PROC_STATS, procStats);
+    }
+
+    /**
+     * Get the {@link DumpsysBatteryStatsItem} of the bugreport.
+     */
+    public DumpsysBatteryStatsItem getBatteryStats() {
+        return (DumpsysBatteryStatsItem) getAttribute(BATTERY_STATS);
+    }
+
+    /**
+     * Get the {@link DumpsysProcStatsItem} of the bugreport.
+     */
+    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
similarity index 62%
rename from src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
rename to 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.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 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 @@
         }
 
         /**
-         * 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 WakeLockCategory getCategory() {
             return (WakeLockCategory) getAttribute(CATEGORY);
         }
+
+        /**
+         * Set the process name holding the wake lock
+         */
+        public void setWakelockProcessName(String processName) {
+            setAttribute(PROCESS_NAME, processName);
+        }
     }
 
-    private Collection<WakeLock> mWakeLocks = new LinkedList<WakeLock>();
-
     /**
-     * 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 @@
     }
 
     /**
-     * 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 @@
     }
 
     /**
+     * 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 @@
     @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 @@
  * 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 @@
      * 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 @@
     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);
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/UnitTests.java b/tests/src/com/android/loganalysis/UnitTests.java
index eba92aa..c896d90 100644
--- a/tests/src/com/android/loganalysis/UnitTests.java
+++ b/tests/src/com/android/loganalysis/UnitTests.java
@@ -16,29 +16,32 @@
 
 package com.android.loganalysis;
 
-import com.android.loganalysis.item.DumpsysBatteryInfoItemTest;
+import com.android.loganalysis.item.BatteryDischargeItemTest;
 import com.android.loganalysis.item.GenericItemTest;
+import com.android.loganalysis.item.InterruptItemTest;
 import com.android.loganalysis.item.MemInfoItemTest;
 import com.android.loganalysis.item.MonkeyLogItemTest;
 import com.android.loganalysis.item.ProcrankItemTest;
 import com.android.loganalysis.item.SmartMonkeyLogItemTest;
 import com.android.loganalysis.item.SystemPropsItemTest;
 import com.android.loganalysis.item.TopItemTest;
+import com.android.loganalysis.item.WakelockItemTest;
 import com.android.loganalysis.parser.AbstractSectionParserTest;
 import com.android.loganalysis.parser.AnrParserTest;
 import com.android.loganalysis.parser.BugreportParserTest;
-import com.android.loganalysis.parser.DumpsysBatteryInfoParserTest;
-import com.android.loganalysis.parser.DumpsysParserTest;
+import com.android.loganalysis.parser.InterruptParserTest;
 import com.android.loganalysis.parser.JavaCrashParserTest;
 import com.android.loganalysis.parser.KernelLogParserTest;
 import com.android.loganalysis.parser.LogcatParserTest;
 import com.android.loganalysis.parser.MemInfoParserTest;
 import com.android.loganalysis.parser.MonkeyLogParserTest;
 import com.android.loganalysis.parser.NativeCrashParserTest;
+import com.android.loganalysis.parser.ProcessUsageParserTest;
 import com.android.loganalysis.parser.ProcrankParserTest;
 import com.android.loganalysis.parser.SystemPropsParserTest;
 import com.android.loganalysis.parser.TopParserTest;
 import com.android.loganalysis.parser.TracesParserTest;
+import com.android.loganalysis.parser.WakelockParserTest;
 import com.android.loganalysis.util.ArrayUtilTest;
 import com.android.loganalysis.util.LogPatternUtilTest;
 import com.android.loganalysis.util.LogTailUtilTest;
@@ -61,31 +64,34 @@
         super();
 
         // item
-        addTestSuite(DumpsysBatteryInfoItemTest.class);
+        addTestSuite(BatteryDischargeItemTest.class);
         addTestSuite(GenericItemTest.class);
+        addTestSuite(InterruptItemTest.class);
         addTestSuite(MemInfoItemTest.class);
         addTestSuite(MonkeyLogItemTest.class);
         addTestSuite(ProcrankItemTest.class);
         addTestSuite(SmartMonkeyLogItemTest.class);
         addTestSuite(SystemPropsItemTest.class);
         addTestSuite(TopItemTest.class);
+        addTestSuite(WakelockItemTest.class);
 
         // parser
         addTestSuite(AbstractSectionParserTest.class);
         addTestSuite(AnrParserTest.class);
         addTestSuite(BugreportParserTest.class);
-        addTestSuite(DumpsysParserTest.class);
-        addTestSuite(DumpsysBatteryInfoParserTest.class);
+        addTestSuite(InterruptParserTest.class);
         addTestSuite(JavaCrashParserTest.class);
         addTestSuite(KernelLogParserTest.class);
         addTestSuite(LogcatParserTest.class);
         addTestSuite(MemInfoParserTest.class);
         addTestSuite(MonkeyLogParserTest.class);
         addTestSuite(NativeCrashParserTest.class);
+        addTestSuite(ProcessUsageParserTest.class);
         addTestSuite(ProcrankParserTest.class);
         addTestSuite(SystemPropsParserTest.class);
         addTestSuite(TopParserTest.class);
         addTestSuite(TracesParserTest.class);
+        addTestSuite(WakelockParserTest.class);
 
         // util
         addTestSuite(ArrayUtilTest.class);
diff --git a/tests/src/com/android/loganalysis/item/BatteryDischargeItemTest.java b/tests/src/com/android/loganalysis/item/BatteryDischargeItemTest.java
new file mode 100644
index 0000000..5e27eb2
--- /dev/null
+++ b/tests/src/com/android/loganalysis/item/BatteryDischargeItemTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.android.loganalysis.item.BatteryDischargeItem.BatteryDischargeInfoItem;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Calendar;
+
+/**
+ * Unit test for {@link BatteryDischargeItem}.
+ */
+public class BatteryDischargeItemTest extends TestCase {
+
+    /**
+     * Test that {@link BatteryDischargeItem#toJson()} returns correctly.
+     */
+    public void testToJson() throws JSONException {
+        BatteryDischargeItem item = new BatteryDischargeItem();
+        item.addBatteryDischargeInfo(Calendar.getInstance(),25, 95);
+
+        // Convert to JSON string and back again
+        JSONObject output = new JSONObject(item.toJson().toString());
+
+        assertTrue(output.has(BatteryDischargeItem.BATTERY_DISCHARGE));
+        assertTrue(output.get(BatteryDischargeItem.BATTERY_DISCHARGE) instanceof JSONArray);
+
+        JSONArray dischargeInfo = output.getJSONArray(BatteryDischargeItem.BATTERY_DISCHARGE);
+
+        assertEquals(1, dischargeInfo.length());
+        assertTrue(dischargeInfo.getJSONObject(0).has(BatteryDischargeInfoItem.BATTERY_LEVEL));
+        assertTrue(dischargeInfo.getJSONObject(0).has(
+                BatteryDischargeInfoItem.DISCHARGE_ELAPSED_TIME));
+        assertTrue(dischargeInfo.getJSONObject(0).has(
+                BatteryDischargeInfoItem.CLOCK_TIME_OF_DISCHARGE));
+
+    }
+}
diff --git a/tests/src/com/android/loganalysis/item/DumpsysBatteryInfoItemTest.java b/tests/src/com/android/loganalysis/item/DumpsysBatteryInfoItemTest.java
deleted file mode 100644
index bb48da7..0000000
--- a/tests/src/com/android/loganalysis/item/DumpsysBatteryInfoItemTest.java
+++ /dev/null
@@ -1,68 +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.item;
-
-import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory;
-
-import junit.framework.TestCase;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Unit test for {@link DumpsysBatteryInfoItem}.
- */
-public class DumpsysBatteryInfoItemTest extends TestCase {
-    private static final String WAKELOCKS = "WAKELOCKS";
-    private static final String LAST_CHARGE_KERNEL_WAKELOCKS = "LAST_CHARGE_KERNEL_WAKELOCKS";
-    private static final String LAST_UNPLUGGED_WAKELOCKS = "LAST_UNPLUGGED_WAKELOCKS";
-    private static final String LAST_UNPLUGGED_KERNEL_WAKELOCKS = "LAST_UNPLUGGED_KERNEL_WAKELOCKS";
-    /**
-     * Test that {@link DumpsysBatteryInfoItem#toJson()} returns correctly.
-     */
-    public void testToJson() throws JSONException {
-        DumpsysBatteryInfoItem item = new DumpsysBatteryInfoItem();
-        item.addWakeLock("a", 0, 1, WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK);
-        item.addWakeLock("b", 2, 3, WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK);
-        item.addWakeLock("c", 4, 5, 6, WakeLockCategory.LAST_UNPLUGGED_WAKELOCK);
-        item.addWakeLock("d", 7, 8, 9, WakeLockCategory.LAST_UNPLUGGED_WAKELOCK);
-        item.addWakeLock("e", 10, 11, 12, WakeLockCategory.LAST_UNPLUGGED_WAKELOCK);
-        item.addWakeLock("w", 0, 1, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK);
-        item.addWakeLock("v", 2, 3, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK);
-        item.addWakeLock("x", 4, 5, 6, WakeLockCategory.LAST_CHARGE_WAKELOCK);
-        item.addWakeLock("y", 7, 8, 9, WakeLockCategory.LAST_CHARGE_WAKELOCK);
-        item.addWakeLock("z", 10, 11, 12, WakeLockCategory.LAST_CHARGE_WAKELOCK);
-
-        // Convert to JSON string and back again
-        JSONObject output = new JSONObject(item.toJson().toString());
-
-        assertTrue(output.has(WAKELOCKS));
-        assertTrue(output.get(WAKELOCKS) instanceof JSONArray);
-
-        JSONArray wakeLocks = output.getJSONArray(WAKELOCKS);
-        assertEquals(10, wakeLocks.length());
-        assertTrue(wakeLocks.get(0) instanceof JSONObject);
-
-        JSONObject wakeLock = wakeLocks.getJSONObject(0);
-        assertEquals("a", wakeLock.get(DumpsysBatteryInfoItem.WakeLock.NAME));
-        assertFalse(wakeLock.has(DumpsysBatteryInfoItem.WakeLock.NUMBER));
-        assertEquals(0, wakeLock.get(DumpsysBatteryInfoItem.WakeLock.HELD_TIME));
-        assertEquals(1, wakeLock.get(DumpsysBatteryInfoItem.WakeLock.LOCKED_COUNT));
-        assertEquals(WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK.toString(),
-                wakeLock.get(DumpsysBatteryInfoItem.WakeLock.CATEGORY));
-    }
-}
diff --git a/tests/src/com/android/loganalysis/item/InterruptItemTest.java b/tests/src/com/android/loganalysis/item/InterruptItemTest.java
new file mode 100644
index 0000000..9e9df61
--- /dev/null
+++ b/tests/src/com/android/loganalysis/item/InterruptItemTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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 com.android.loganalysis.item.InterruptItem.InterruptInfoItem;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Unit test for {@link InterruptItem}.
+ */
+public class InterruptItemTest extends TestCase {
+
+    /**
+     * Test that {@link InterruptItem#toJson()} returns correctly.
+     */
+    public void testToJson() throws JSONException {
+        InterruptItem item = new InterruptItem();
+        item.addInterrupt("smd-modem",25, InterruptItem.InterruptCategory.ALARM_INTERRUPT);
+
+        // Convert to JSON string and back again
+        JSONObject output = new JSONObject(item.toJson().toString());
+
+        assertTrue(output.has(InterruptItem.INTERRUPTS));
+        assertTrue(output.get(InterruptItem.INTERRUPTS) instanceof JSONArray);
+
+        JSONArray interruptsInfo = output.getJSONArray(InterruptItem.INTERRUPTS);
+
+        assertEquals(1, interruptsInfo.length());
+        assertTrue(interruptsInfo.getJSONObject(0).has(InterruptInfoItem.NAME));
+        assertTrue(interruptsInfo.getJSONObject(0).has(InterruptInfoItem.CATEGORY));
+        assertTrue(interruptsInfo.getJSONObject(0).has(InterruptInfoItem.INTERRUPT_COUNT));
+
+    }
+}
diff --git a/tests/src/com/android/loganalysis/item/WakelockItemTest.java b/tests/src/com/android/loganalysis/item/WakelockItemTest.java
new file mode 100644
index 0000000..f570a7b
--- /dev/null
+++ b/tests/src/com/android/loganalysis/item/WakelockItemTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.android.loganalysis.item.WakelockItem.WakelockInfoItem;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Unit test for {@link WakelockItem}.
+ */
+public class WakelockItemTest extends TestCase {
+
+    /**
+     * Test that {@link WakelockItem#toJson()} returns correctly.
+     */
+    public void testToJson() throws JSONException {
+        WakelockItem item = new WakelockItem();
+        item.addWakeLock("screen","u100", 150000, 25,
+                WakelockItem.WakeLockCategory.PARTIAL_WAKELOCK);
+        item.addWakeLock("wlan_rx", 150000, 25,
+            WakelockItem.WakeLockCategory.KERNEL_WAKELOCK);
+
+        // Convert to JSON string and back again
+        JSONObject output = new JSONObject(item.toJson().toString());
+
+        assertTrue(output.has(WakelockItem.WAKELOCKS));
+        assertTrue(output.get(WakelockItem.WAKELOCKS) instanceof JSONArray);
+
+        JSONArray wakelockInfo = output.getJSONArray(WakelockItem.WAKELOCKS);
+
+        assertEquals(2, wakelockInfo.length());
+        assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.NAME));
+        assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.PROCESS_UID));
+        assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.HELD_TIME));
+        assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.LOCKED_COUNT));
+        assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.CATEGORY));
+
+        assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.NAME));
+        assertFalse(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.PROCESS_UID));
+        assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.HELD_TIME));
+        assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.LOCKED_COUNT));
+        assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.CATEGORY));
+
+    }
+}
diff --git a/tests/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParserTest.java b/tests/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParserTest.java
new file mode 100644
index 0000000..b176fa2
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParserTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link BatteryStatsDetailedInfoParser}
+ */
+public class BatteryStatsDetailedInfoParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testBatteryStatsDetailedInfoParser() {
+        List<String> inputBlock = Arrays.asList(
+                " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime",
+                " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime",
+                " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime",
+                " All kernel wake locks:",
+                " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime",
+                " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime",
+                " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime",
+                " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime",
+                " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime",
+                " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime",
+                " ",
+                " All partial wake locks:",
+                " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime",
+                " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime",
+                " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime",
+                " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime",
+                " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime",
+                " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime",
+                " ",
+                " All wakeup reasons:",
+                " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime",
+                " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime",
+                " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime",
+                " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime",
+                " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime",
+                " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime",
+                " Wakeup reason unknown: 8s 455ms (0 times) realtime",
+                " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime",
+                " ",
+                " 0:",
+                "    User activity: 2 other",
+                "    Wake lock SCREEN_FROZEN realtime",
+                "    Sensor 0: 9s 908ms realtime (1 times)",
+                "    Sensor 1: 9s 997ms realtime (1 times)",
+                "    Foreground for: 2h 21m 5s 622ms",
+                "    Apk android:",
+                "      24 wakeup alarms",
+                " u0a9:",
+                "    Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)",
+                "    Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp",
+                "    Sensor 2: 12m 13s 15ms realtime (5 times)",
+                "    Sensor 32: (not used)",
+                "    Sensor 35: (not used)");
+
+        BatteryStatsDetailedInfoItem stats = new BatteryStatsDetailedInfoParser().parse(inputBlock);
+
+        assertEquals(8465622, stats.getTimeOnBattery());
+        assertEquals(910619, stats.getScreenOnTime());
+        assertNotNull(stats.getWakelockItem());
+        assertNotNull(stats.getInterruptItem());
+        assertNotNull(stats.getProcessUsageItem());
+    }
+
+    /**
+     * Test with missing wakelock section
+     */
+    public void testMissingWakelockSection() {
+        List<String> inputBlock = Arrays.asList(
+                " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime",
+                " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime",
+                " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime",
+                " All wakeup reasons:",
+                " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime",
+                " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime",
+                " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime",
+                " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime",
+                " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime",
+                " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime",
+                " Wakeup reason unknown: 8s 455ms (0 times) realtime",
+                " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime",
+                " ",
+                " 0:",
+                "    User activity: 2 other",
+                "    Wake lock SCREEN_FROZEN realtime",
+                "    Sensor 0: 9s 908ms realtime (1 times)",
+                "    Sensor 1: 9s 997ms realtime (1 times)",
+                "    Foreground for: 2h 21m 5s 622ms",
+                "    Apk android:",
+                "      24 wakeup alarms",
+                " u0a9:",
+                "    Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)",
+                "    Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp",
+                "    Sensor 2: 12m 13s 15ms realtime (5 times)",
+                "    Sensor 32: (not used)",
+                "    Sensor 35: (not used)");
+        BatteryStatsDetailedInfoItem stats = new BatteryStatsDetailedInfoParser().parse(inputBlock);
+
+        assertEquals(8465622, stats.getTimeOnBattery());
+        assertEquals(910619, stats.getScreenOnTime());
+
+        assertNull(stats.getWakelockItem());
+
+        assertNotNull(stats.getInterruptItem());
+        assertNotNull(stats.getProcessUsageItem());
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParserTest.java b/tests/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParserTest.java
new file mode 100644
index 0000000..ad0c999
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParserTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.BatteryStatsSummaryInfoItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link BatteryStatsSummaryInfoParser}
+ */
+public class BatteryStatsSummaryInfoParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testBatteryStatsSummaryInfoParser() {
+        List<String> inputBlock = Arrays.asList(
+                "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):",
+                "     0 (9) RESET:TIME: 2014-12-09-11-33-29",
+                "     +1s067ms (1) 100 c0500020 -wifi_full_lock -wifi_scan",
+                "     +3s297ms (2) 100 80400020 -wake_lock -screen",
+                "     +30m02s075ms (1) 100 c0500020 wifi_signal_strength=4 wifi_suppl=completed",
+                "     +30m03s012ms (2) 099 c0500020 temp=306 volt=4217",
+                "     +33m48s967ms (1) 099 f8400020 +wifi_scan",
+                "     +33m49s335ms (2) 098 f0400020 temp=324 -wifi_scan",
+                "     +1h07m27s735ms (1) 098 80400020 -wake_lock",
+                "     +1h07m27s950ms (2) 097 c0400020",
+                "     +1h07m29s000ms (2) 097 c0400020 -sync=u0a41:\"gmail-ls/com.google/a@g",
+                "     +1h25m34s877ms (2) 097 00400020 -running wake_reason=0:200:qcom,smd-rpm",
+                "     +1h25m41s948ms (2) 096 80400020 wifi_suppl=associated",
+                "     +2h13m40s055ms (1) 096 00400018 -running",
+                "     +2h13m40s570ms (2) 095 c0400008 temp=304 volt=4167",
+                "     +2h56m50s792ms (1) 095 80400020 -wake_lock",
+                "     +2h56m50s967ms (2) 094 00400020 temp=317 -running",
+                "     +3h38m57s986ms (2) 094 80400020 +running wake_reason=0:289:bcmsdh_sdmmc",
+                "     +3h38m58s241ms (2) 093 00400020 temp=327 -running",
+                "     +3h56m33s329ms (1) 093 00400020 -running -wake_lock",
+                "     +3h56m43s245ms (2) 092 00400020 -running",
+                "     +4h13m00s551ms (1) 092 00400020 -running -wake_lock",
+                "     +4h13m24s250ms (2) 091 00400020 -running",
+                "     +4h34m52s233ms (2) 091 80400020 +running wake_reason=0:289:bcmsdh_sdmmc",
+                "     +4h34m52s485ms (3) 090 00400020 -running wake_reason=0:200:qcom,smd-rpm",
+                "     +4h57m20s644ms (1) 090 00400020 -running",
+                "     +4h57m38s484ms (2) 089 00400020 -running",
+                "     +5h20m58s226ms (1) 089 80400020 +running wifi_suppl=associated",
+                "     +5h21m03s909ms (1) 088 80400020 -wake_lock -wifi_full_lock",
+                "     +5h40m38s169ms (2) 088 c0500020 +top=u0a19:com.google.android.googlequick",
+                "     +5h40m38s479ms (2) 087 c0500020 volt=4036",
+                "     +6h16m45s248ms (2) 087 d0440020 -sync=u0a41:gmail-ls/com.google/avellore@go",
+                "     +6h16m45s589ms (2) 086 d0440020 volt=4096",
+                "     +6h52m43s316ms (1) 086 80400020 -wake_lock",
+                "     +6h53m18s952ms (2) 085 c0400020",
+                "     +7h24m02s415ms (1) 085 80400020 -wake_lock",
+                "     +7h24m02s904ms (3) 084 c0400020 volt=4105 +wake_lock=u0a7:NlpWakeLock",
+                "     +7h29m10s379ms (1) 084 00400020 -running -wake_lock",
+                "     +7h29m11s841ms (2) 083 00400020 temp=317 volt=4047 -running",
+                "     +7h41m08s963ms (1) 083 00400020 -running",
+                "     +7h41m20s494ms (2) 082 00400020 temp=300 -running",
+                "     +7h54m57s288ms (1) 082 52441420 -running",
+                "     +7h55m00s801ms (1) 081 52441420 -running",
+                "     +8h02m18s594ms (1) 081 50440020 -running",
+                "     +8h02m23s493ms (2) 080 50440020 temp=313 -running");
+
+        BatteryStatsSummaryInfoItem summary = new BatteryStatsSummaryInfoParser().parse(inputBlock);
+
+        assertEquals("The battery dropped a level 24 mins on average",
+                summary.getBatteryDischargeRate());
+
+        assertEquals("The peak discharge time was during Tue Dec 09 16:31:07 PST 2014 to "
+                + "Tue Dec 09 19:35:52 PST 2014 where battery dropped from 89 to 80",
+                summary.getPeakDischargeTime());
+    }
+
+    public void testNoBatteryDischarge() {
+        List<String> inputBlock = Arrays.asList(
+                "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):",
+                "     0 (9) RESET:TIME: 2014-12-09-11-33-29");
+        BatteryStatsSummaryInfoItem summary = new BatteryStatsSummaryInfoParser().parse(inputBlock);
+
+        assertEquals("The battery did not discharge", summary.getBatteryDischargeRate());
+        assertEquals("The battery did not discharge", summary.getPeakDischargeTime());
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/BugreportParserTest.java b/tests/src/com/android/loganalysis/parser/BugreportParserTest.java
index a17493e..ea7537f 100644
--- a/tests/src/com/android/loganalysis/parser/BugreportParserTest.java
+++ b/tests/src/com/android/loganalysis/parser/BugreportParserTest.java
@@ -118,14 +118,24 @@
                 "------ SECTION ------",
                 "",
                 "------ DUMPSYS (dumpsys) ------",
-                "DUMP OF SERVICE batteryinfo:",
-                "Statistics since last unplugged:",
-                "  Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
-                "  Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
+                "DUMP OF SERVICE batterystats:",
+                "Battery History (0% used, 1636 used of 256KB, 15 strings using 794):",
+                "    0 (15) RESET:TIME: 1970-01-10-06-23-28",
+                "          +45s702ms (2) 001 80080000 volt=4187",
+                "          +1m15s525ms (2) 001 80080000 temp=299 volt=4155",
+                "Statistics since last charged:",
+                "  Time on battery: 1h 5m 2s 4ms (9%) realtime, 1h 5m 2s 4ms (9%) uptime", 
+                " Time on battery screen off: 1h 4m 5s 8ms (9%) realtime, 1h 4m 5s 8ms (9%) uptime",
+                " All kernel wake locks:",
+                " Kernel Wake lock PowerManagerService.WakeLocks: 5m 10s 6ms (2 times) realtime",
+                " Kernel Wake lock msm_serial_hs_rx: 2m 13s 612ms (258 times) realtime",
                 "",
                 "  All partial wake locks:",
-                "  Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
-                "  Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
+                "  Wake lock 1001 ProxyController: 1h 4m 47s 565ms (4 times) realtime",
+                "  Wake lock 1013 AudioMix: 1s 979ms (3 times) realtime",
+                "  All wakeup reasons:",
+                "  Wakeup reason 2:bcmsdh_sdmmc:2:qcom,smd:2:msmgio: 1m 5s 4ms (2 times) realtime", 
+                "  Wakeup reason 2:qcom,smd-rpm:2:fc4c.qcom,spmi: 7m 1s 914ms (7 times) realtime",
                 "");
 
         BugreportItem bugreport = new BugreportParser().parse(lines);
@@ -158,7 +168,7 @@
         assertEquals(4, bugreport.getSystemProps().size());
 
         assertNotNull(bugreport.getDumpsys());
-        assertNotNull(bugreport.getDumpsys().getBatteryInfo());
+        assertNotNull(bugreport.getDumpsys().getBatteryStats());
     }
 
     /**
@@ -440,7 +450,7 @@
 
         bugreport = new BugreportParser().parse(lines);
         assertNotNull(bugreport);
-        assertNull(bugreport.getDumpsys());
+        assertNotNull(bugreport.getDumpsys());
         assertNull(bugreport.getKernelLog());
         assertNull(bugreport.getLastKmsg());
         assertNull(bugreport.getMemInfo());
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
deleted file mode 100644
index 66c7796..0000000
--- a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
+++ /dev/null
@@ -1,188 +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.WakeLock;
-import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory;
-
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Unit tests for {@link DumpsysBatteryInfoParser}
- */
-public class DumpsysBatteryInfoParserTest extends TestCase {
-
-    /**
-     * Test that complete battery info dumpsys is parsed.
-     */
-    public void testParse() {
-        List<String> inputBlock = Arrays.asList(
-                "Battery History:",
-                "         -15m07s754ms  START",
-                "         -15m05s119ms 088 20080000 status=charging health=good plug=usb temp=269 volt=4358 +plugged +sensor",
-                "",
-                "Per-PID Stats:",
-                "  PID 4242 wake time: +5m10s24ms",
-                "  PID 543 wake time: +3s585ms",
-                "",
-                "Statistics since last charge:",
-                "  System starts: 2, currently on battery: false",
-                "  Time on battery: 8m 20s 142ms (55.1%) realtime, 5m 17s 5ms (34.9%) uptime",
-                "  Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
-                "  Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
-                "  ",
-                "  All partial wake locks:",
-                "  Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
-                "  Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
-                "",
-                "Statistics since last unplugged:",
-                "  Time on battery: 8m 20s 142ms (92.6%) realtime, 5m 17s 5ms (58.7%) uptime",
-                "  Total run time: 8m 59s 968ms realtime, 5m 56s 831ms uptime, ",
-                "  Screen on: 0ms (0.0%), Input events: 0, Active phone call: 0ms (0.0%)",
-                "  Screen brightnesses: No activity",
-                "  Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
-                "  Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
-                "  Kernel Wake lock \"main\": 7s 323ms (0 times) realtime",
-                "  Total received: 0B, Total sent: 0B",
-                "  Total full wakelock time: 0ms , Total partial wakelock time: 5m 10s 60ms ",
-                "  Signal levels: No activity",
-                "  Signal scanning time: 0ms ",
-                "  Radio types: none 8m 20s 142ms (100.0%) 0x",
-                "  Radio data uptime when unplugged: 0 ms",
-                "  Wifi on: 0ms (0.0%), Wifi running: 0ms (0.0%), Bluetooth on: 0ms (0.0%)",
-                " ",
-                "  Device is currently plugged into power",
-                "    Last discharge cycle start level: 87",
-                "    Last discharge cycle end level: 87",
-                "    Amount discharged while screen on: 0",
-                "    Amount discharged while screen off: 0",
-                " ",
-                "  All partial wake locks:",
-                "  Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
-                "  Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
-                "  Wake lock #1000 show keyguard: 277ms (1 times) realtime",
-                "  Wake lock #1000 ActivityManager-Sleep: 72ms (1 times) realtime",
-                "  Wake lock #10015 AlarmManager: 16ms (1 times) realtime",
-                "",
-                "  #0:",
-                "    Wake lock partialWakelock: 5m 9s 260ms partial (1 times) realtime",
-                "    Proc /init:",
-                "      CPU: 10ms usr + 0ms krn",
-                "    Proc flush-179:0:",
-                "      CPU: 0ms usr + 10ms krn",
-                "    Proc vold:",
-                "      CPU: 20ms usr + 10ms krn",
-                "  #1000:",
-                "    User activity: 3 other, 1 button",
-                "    Wake lock show keyguard: 277ms partial (1 times) realtime",
-                "    Wake lock AlarmManager: 422ms partial (7 times) realtime");
-
-        DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
-        DumpsysBatteryInfoItem item = parser.parse(inputBlock);
-
-        assertEquals(2, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).size());
-        assertEquals(2, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size());
-        assertEquals(5, item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_WAKELOCK).size());
-        assertEquals(3, item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK).size());
-
-        assertEquals("partialWakelock",
-                item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).get(0).getName());
-        assertEquals("PowerManagerService.WakeLocks",
-                item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).get(0).getName());
-        assertEquals("partialWakelock",
-                item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_WAKELOCK).get(0).getName());
-        assertEquals("PowerManagerService.WakeLocks",
-                item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK).get(0).getName());
-    }
-
-    /**
-     * Test that kernel wakelocks are parsed.
-     */
-    public void testParseKernelWakeLock() {
-        String inputLine = "  Kernel Wake lock \"Process\": 1d 2h 3m 4s 5ms (6 times) realtime";
-
-        DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
-        parser.parseKernelWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK);
-        DumpsysBatteryInfoItem item = parser.getItem();
-
-        assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size());
-        WakeLock wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).get(0);
-        assertEquals("Process", wakeLock.getName());
-        assertNull(wakeLock.getNumber());
-        assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime());
-        assertEquals(6, wakeLock.getLockedCount());
-
-        inputLine = "  Kernel Wake lock \"Process\": 5m 7ms (2 times) realtime";
-
-        parser = new DumpsysBatteryInfoParser();
-        parser.parseKernelWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK);
-        item = parser.getItem();
-
-        assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size());
-        wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).get(0);
-        assertEquals("Process", wakeLock.getName());
-        assertNull(wakeLock.getNumber());
-        assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime());
-        assertEquals(2, wakeLock.getLockedCount());
-    }
-
-    /**
-     * Test that wake locks are parsed.
-     */
-    public void testParseWakeLock() {
-        String inputLine = "  Wake lock #1234 Process: 1d 2h 3m 4s 5ms (6 times) realtime";
-
-        DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
-        parser.parseWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_WAKELOCK);
-        DumpsysBatteryInfoItem item = parser.getItem();
-
-        assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).size());
-        WakeLock wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).get(0);
-        assertEquals("Process", wakeLock.getName());
-        assertEquals((Integer) 1234, wakeLock.getNumber());
-        assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime());
-        assertEquals(6, wakeLock.getLockedCount());
-
-        inputLine = "  Wake lock #1234 Process:with:colons: 5m 7ms (2 times) realtime";
-
-        parser = new DumpsysBatteryInfoParser();
-        parser.parseWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_WAKELOCK);
-        item = parser.getItem();
-
-        assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).size());
-        wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).get(0);
-        assertEquals("Process:with:colons", wakeLock.getName());
-        assertEquals((Integer) 1234, wakeLock.getNumber());
-        assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime());
-        assertEquals(2, wakeLock.getLockedCount());
-    }
-
-    /**
-     * Test the helper function to covert time to ms.
-     */
-    public void testGetMs() {
-        assertEquals(1, DumpsysBatteryInfoParser.getMs(0, 0, 0, 0, 1));
-        assertEquals(1000, DumpsysBatteryInfoParser.getMs(0, 0, 0, 1, 0));
-        assertEquals(60 * 1000, DumpsysBatteryInfoParser.getMs(0, 0, 1, 0, 0));
-        assertEquals(60 * 60 * 1000, DumpsysBatteryInfoParser.getMs(0, 1, 0, 0, 0));
-        assertEquals(24 * 60 * 60 * 1000, DumpsysBatteryInfoParser.getMs(1, 0, 0, 0, 0));
-    }
-}
-
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysBatteryStatsParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysBatteryStatsParserTest.java
new file mode 100644
index 0000000..8799a9b
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/DumpsysBatteryStatsParserTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.DumpsysBatteryStatsItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DumpsysBatteryStatsParser}
+ */
+public class DumpsysBatteryStatsParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testBatteryStatsParser() {
+        List<String> inputBlock = Arrays.asList(
+                "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):",
+                "     0 (9) RESET:TIME: 2014-12-09-11-33-29",
+                "     +1s067ms (1) 100 c0500020 -wifi_full_lock -wifi_scan",
+                "     +3s297ms (2) 100 80400020 -wake_lock -screen",
+                "     +30m02s075ms (1) 100 c0500020 wifi_signal_strength=4 wifi_suppl=completed",
+                "     +30m03s012ms (2) 099 c0500020 temp=306 volt=4217",
+                "     +33m48s967ms (1) 099 f8400020 +wifi_scan",
+                "     +33m49s335ms (2) 098 f0400020 temp=324 -wifi_scan",
+                "Statistics since last charge:",
+                " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime",
+                " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime",
+                " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime",
+                " All kernel wake locks:",
+                " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime",
+                " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime",
+                " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime",
+                " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime",
+                " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime",
+                " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime",
+                " ",
+                " All partial wake locks:",
+                " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime",
+                " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime",
+                " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime",
+                " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime",
+                " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime",
+                " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime",
+                " ",
+                " All wakeup reasons:",
+                " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime",
+                " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime",
+                " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime",
+                " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime",
+                " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime",
+                " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime",
+                " Wakeup reason unknown: 8s 455ms (0 times) realtime",
+                " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime",
+                " ",
+                " 0:",
+                "    User activity: 2 other",
+                "    Wake lock SCREEN_FROZEN realtime",
+                "    Sensor 0: 9s 908ms realtime (1 times)",
+                "    Sensor 1: 9s 997ms realtime (1 times)",
+                "    Foreground for: 2h 21m 5s 622ms",
+                "    Apk android:",
+                "      24 wakeup alarms",
+                " u0a9:",
+                "    Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)",
+                "    Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp",
+                "    Sensor 2: 12m 13s 15ms realtime (5 times)",
+                "    Sensor 32: (not used)",
+                "    Sensor 35: (not used)");
+
+        DumpsysBatteryStatsItem batteryStats = new DumpsysBatteryStatsParser().parse(inputBlock);
+        assertNotNull(batteryStats.getBatteryStatsSummaryItem());
+        assertNotNull(batteryStats.getDetailedBatteryStatsItem());
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
index 423ec06..7dff3a1 100644
--- a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
+++ b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.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.
@@ -15,7 +15,6 @@
  */
 package com.android.loganalysis.parser;
 
-import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory;
 import com.android.loganalysis.item.DumpsysItem;
 
 import junit.framework.TestCase;
@@ -29,40 +28,75 @@
 public class DumpsysParserTest extends TestCase {
 
     /**
-     * Test that the dumpsys section of the bugreport is parsed.
+     * Test that normal input is parsed.
      */
-    public void testParse() {
+    public void testDumpsysParser() {
         List<String> inputBlock = Arrays.asList(
-                "-------------------------------------------------------------------------------",
-                "DUMP OF SERVICE process1:",
-                "-------------------------------------------------------------------------------",
-                "DUMP OF SERVICE batteryinfo:",
+                "DUMP OF SERVICE batterystats:",
+                "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):",
+                "     0 (9) RESET:TIME: 2014-12-09-11-33-29",
+                "     +1s067ms (1) 100 c0500020 -wifi_full_lock -wifi_scan",
+                "     +3s297ms (2) 100 80400020 -wake_lock -screen",
+                "     +30m02s075ms (1) 100 c0500020 wifi_signal_strength=4 wifi_suppl=completed",
+                "     +30m03s012ms (2) 099 c0500020 temp=306 volt=4217",
+                "     +33m48s967ms (1) 099 f8400020 +wifi_scan",
+                "     +33m49s335ms (2) 098 f0400020 temp=324 -wifi_scan",
                 "Statistics since last charge:",
-                "  Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
-                "  Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
-                "",
-                "  All partial wake locks:",
-                "  Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
-                "  Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
-                "",
-                "-------------------------------------------------------------------------------",
-                "DUMP OF SERVICE process2:",
-                "-------------------------------------------------------------------------------");
+                " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime",
+                " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime",
+                " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime",
+                " All kernel wake locks:",
+                " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime",
+                " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime",
+                " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime",
+                " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime",
+                " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime",
+                " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime",
+                " ",
+                " All partial wake locks:",
+                " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime",
+                " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime",
+                " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime",
+                " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime",
+                " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime",
+                " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime",
+                " ",
+                " All wakeup reasons:",
+                " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime",
+                " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime",
+                " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime",
+                " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime",
+                " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime",
+                " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime",
+                " Wakeup reason unknown: 8s 455ms (0 times) realtime",
+                " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime",
+                " ",
+                " 0:",
+                "    User activity: 2 other",
+                "    Wake lock SCREEN_FROZEN realtime",
+                "    Sensor 0: 9s 908ms realtime (1 times)",
+                "    Sensor 1: 9s 997ms realtime (1 times)",
+                "    Foreground for: 2h 21m 5s 622ms",
+                "    Apk android:",
+                "      24 wakeup alarms",
+                " u0a9:",
+                "    Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)",
+                "    Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp",
+                "    Sensor 2: 12m 13s 15ms realtime (5 times)",
+                "    Sensor 32: (not used)",
+                "    Sensor 35: (not used)",
+                "DUMP OF SERVICE procstats:",
+                "COMMITTED STATS FROM 2015-03-20-02-01-02 (checked in):",
+                "  * com.android.systemui / u0a22 / v22:",
+                "           TOTAL: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)",
+                "      Persistent: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)",
+                "  * com.google.process.gapps / u0a9 / v22:",
+                "           TOTAL: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)",
+                "          Imp Fg: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)");
 
-        DumpsysItem item = new DumpsysParser().parse(inputBlock);
-
-        assertNotNull(item.getBatteryInfo());
-        assertEquals(2, item.getBatteryInfo().getWakeLocks(
-                WakeLockCategory.LAST_CHARGE_WAKELOCK).size());
-        assertEquals(2, item.getBatteryInfo().getWakeLocks(
-                WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size());
-    }
-
-    /**
-     * Test that an empty input returns {@code null}.
-     */
-    public void testEmptyInput() {
-        DumpsysItem item = new DumpsysParser().parse(Arrays.asList(""));
-        assertNull(item);
+        DumpsysItem dumpsys = new DumpsysParser().parse(inputBlock);
+        assertNotNull(dumpsys.getBatteryStats());
+        assertNotNull(dumpsys.getProcStats());
     }
 }
+
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysProcStatsParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysProcStatsParserTest.java
new file mode 100644
index 0000000..27bb7a4
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/DumpsysProcStatsParserTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.DumpsysProcStatsItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DumpsysProcStatsParser}
+ */
+public class DumpsysProcStatsParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testDumpsysProcStatsParser() {
+        List<String> inputBlock = Arrays.asList(
+                "COMMITTED STATS FROM 2015-03-20-02-01-02 (checked in):",
+                "  * com.android.systemui / u0a22 / v22:",
+                "           TOTAL: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)",
+                "      Persistent: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)",
+                "  * com.google.process.gapps / u0a9 / v22:",
+                "           TOTAL: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)",
+                "          Imp Fg: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)");
+
+        DumpsysProcStatsItem procstats = new DumpsysProcStatsParser().parse(inputBlock);
+        assertEquals("com.android.systemui", procstats.get("u0a22"));
+        assertEquals("com.google.process.gapps", procstats.get("u0a9"));
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/InterruptParserTest.java b/tests/src/com/android/loganalysis/parser/InterruptParserTest.java
new file mode 100644
index 0000000..d45f3af
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/InterruptParserTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link InterruptParser}
+ */
+public class InterruptParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testInterruptParser() {
+        List<String> inputBlock = Arrays.asList(
+               " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime",
+               " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime",
+               " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime",
+               " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime",
+               " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime",
+               " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime",
+               " Wakeup reason unknown: 8s 455ms (0 times) realtime",
+               " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime");
+
+        InterruptItem interrupt = new InterruptParser().parse(inputBlock);
+
+        assertEquals(1, interrupt.getInterrupts(
+                InterruptItem.InterruptCategory.WIFI_INTERRUPT).size());
+
+        assertEquals("9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso", interrupt.getInterrupts(
+                InterruptItem.InterruptCategory.WIFI_INTERRUPT).get(0).getName());
+
+        assertEquals(2, interrupt.getInterrupts(
+                InterruptItem.InterruptCategory.MODEM_INTERRUPT).size());
+
+        assertEquals(5, interrupt.getInterrupts(InterruptItem.InterruptCategory.MODEM_INTERRUPT).
+                get(0).getInterruptCount());
+
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/ProcessUsageParserTest.java b/tests/src/com/android/loganalysis/parser/ProcessUsageParserTest.java
new file mode 100644
index 0000000..e87b4a1
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/ProcessUsageParserTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.ProcessUsageInfoItem;
+import com.android.loganalysis.item.ProcessUsageItem.SensorInfoItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link ProcessUsageParser}
+ */
+public class ProcessUsageParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testProcessUsageParser() {
+        List<String> inputBlock = Arrays.asList(
+               " 0:",
+               "    Mobile network: 173.70KB received, 102.55KB sent (packets 129)",
+               "    Mobile radio active: 6m 5s 80ms (14.9%) 80x @ 139 mspp",
+               " 1000:",
+               "  Mobile network: 16.43KB received, 26.26KB sent",
+               "  Mobile radio active: 1m 17s 489ms (3.2%) 61x @ 179 mspp",
+               "  Sensor 44: 27m 18s 207ms realtime (22 times)",
+               "  Sensor 36: 6s 483ms realtime (3 times)",
+               "  Proc servicemanager:",
+               "      CPU: 2s 20ms usr + 4s 60ms krn ; 0ms fg",
+               "    Apk android:",
+               "      266 wakeup alarms",
+               " u0a2:",
+               "  Mobile network: 16.43KB received, 26.26KB sent",
+               "  Mobile radio active: 1m 17s 489ms (3.2%) 61x @ 179 mspp",
+               "  Sensor 0: 5s 207ms realtime (2 times)",
+               "  Proc servicemanager:",
+               "      CPU: 2s 20ms usr + 4s 60ms krn ; 0ms fg",
+               "    Apk android:",
+               "      2 wakeup alarms");
+
+        ProcessUsageItem processUsage = new ProcessUsageParser().parse(inputBlock);
+
+        assertEquals(2, processUsage.getProcessUsage().size());
+
+        LinkedList<ProcessUsageInfoItem> processUsageInfo =
+                (LinkedList<ProcessUsageInfoItem>)processUsage.getProcessUsage();
+
+        assertEquals("1000", processUsageInfo.get(1).getProcessUID());
+        assertEquals(266, processUsageInfo.get(1).getAlarmWakeups());
+
+        LinkedList<SensorInfoItem> sensor = processUsageInfo.get(1).getSensorUsage();
+        assertEquals("44", sensor.get(0).getSensorName());
+        assertEquals("36", sensor.get(1).getSensorName());
+    }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/WakelockParserTest.java b/tests/src/com/android/loganalysis/parser/WakelockParserTest.java
new file mode 100644
index 0000000..bed831b
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/WakelockParserTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link WakelockParser}
+ */
+public class WakelockParserTest extends TestCase {
+
+    /**
+     * Test that normal input is parsed.
+     */
+    public void testWakelockParser() {
+        List<String> inputBlock = Arrays.asList(
+               " All kernel wake locks:",
+               " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime",
+               " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime",
+               " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime",
+               " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime",
+               " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime",
+               " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime",
+               " ",
+               " All partial wake locks:",
+               " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime",
+               " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime",
+               " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime",
+               " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime",
+               " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime",
+               " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime");
+
+        WakelockItem wakelock = new WakelockParser().parse(inputBlock);
+
+        assertEquals(WakelockParser.TOP_WAKELOCK_COUNT,
+                wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK).size());
+        assertEquals("event0-2656 ",
+                wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK).get(0).getName());
+        assertEquals(229268, wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK).
+                get(0).getHeldTime());
+        assertEquals(2399, wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK).
+                get(0).getLockedCount());
+
+        assertEquals(WakelockParser.TOP_WAKELOCK_COUNT,
+                wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK).size());
+        assertEquals("NlpWakeLock", wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK).
+                get(0).getName());
+        assertEquals("u0a7", wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK).
+                get(0).getProcessUID());
+        assertEquals(493203, wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK).
+                get(0).getHeldTime());
+        assertEquals(1479, wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK).
+                get(0).getLockedCount());
+    }
+}
+