1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.loganalysis.parser; 17 18 import com.android.loganalysis.item.BatteryDischargeItem; 19 import com.android.loganalysis.item.BatteryDischargeItem.BatteryDischargeInfoItem; 20 import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; 21 import com.android.loganalysis.util.NumberFormattingUtil; 22 23 import java.util.Calendar; 24 import java.util.GregorianCalendar; 25 import java.util.LinkedList; 26 import java.util.List; 27 import java.util.Queue; 28 import java.util.regex.Matcher; 29 import java.util.regex.Pattern; 30 31 /** 32 * A {@link IParser} to parse batterystats summary 33 */ 34 public class BatteryStatsSummaryInfoParser implements IParser{ 35 36 /** 37 * Matches: 0 (15) RESET:TIME: 2015-01-18-12-56-57 38 */ 39 private static final Pattern RESET_TIME_PATTERN = Pattern.compile("^\\s*" 40 + "\\d\\s*\\(\\d+\\)\\s*RESET:TIME:\\s*(\\d+)-(\\d+)-(\\d+)-(\\d+)-(\\d+)-(\\d+)$"); 41 42 /** 43 * Matches: +1d01h03m37s246ms (1) 028 c10400010 -running -wake_lock 44 */ 45 private static final Pattern BATTERY_DISCHARGE_PATTERN = Pattern.compile( 46 "^\\s*\\+(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?(?:(\\d+)ms)? \\(\\d+\\) " 47 + "(\\d+) \\w+ .*"); 48 49 private BatteryDischargeItem mBatteryDischarge = new BatteryDischargeItem(); 50 private BatteryStatsSummaryInfoItem mItem = new BatteryStatsSummaryInfoItem(); 51 private long mBatteryDischargeRateAvg = 0; 52 private int mBatteryDischargeSamples = 0; 53 private Calendar mResetTime; 54 private static final int BATTERY_GROUP_LIMIT = 10; 55 56 /** 57 * {@inheritDoc} 58 * 59 * @return The {@link BatteryStatsSummaryInfoItem}. 60 */ 61 @Override parse(List<String> lines)62 public BatteryStatsSummaryInfoItem parse(List<String> lines) { 63 Matcher resetTimeMatcher = null; 64 Matcher dischargeMatcher = null; 65 66 long previousDischargeElapsedTime= 0; 67 int previousBatteryLevel = 0; 68 boolean batteryDischargedFully = false; 69 for (String line : lines) { 70 resetTimeMatcher = RESET_TIME_PATTERN.matcher(line); 71 dischargeMatcher = BATTERY_DISCHARGE_PATTERN.matcher(line); 72 if (resetTimeMatcher.matches()) { 73 mResetTime = new GregorianCalendar(); 74 final int year = Integer.parseInt(resetTimeMatcher.group(1)); 75 final int month = Integer.parseInt(resetTimeMatcher.group(2)); 76 final int day = Integer.parseInt(resetTimeMatcher.group(3)); 77 final int hour = Integer.parseInt(resetTimeMatcher.group(4)); 78 final int minute = Integer.parseInt(resetTimeMatcher.group(5)); 79 final int second = Integer.parseInt(resetTimeMatcher.group(6)); 80 // Calendar month is zero indexed but the parsed date is 1-12 81 mResetTime.set(year, (month - 1), day, hour, minute, second); 82 } else if (dischargeMatcher.matches()) { 83 final int days = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(1)); 84 final int hours = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(2)); 85 final int mins = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(3)); 86 final int secs = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(4)); 87 final int msecs = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(5)); 88 final int batteryLevel = Integer.parseInt(dischargeMatcher.group(6)); 89 if (batteryLevel == 0) { 90 // Ignore the subsequent battery drop readings 91 batteryDischargedFully = true; 92 continue; 93 } else if (previousBatteryLevel == 0) { 94 // Ignore the first drop 95 previousBatteryLevel = batteryLevel; 96 continue; 97 } else if (!batteryDischargedFully && previousBatteryLevel != batteryLevel) { 98 long elapsedTime = NumberFormattingUtil.getMs(days, hours, mins, secs, msecs); 99 mBatteryDischargeRateAvg += (elapsedTime - previousDischargeElapsedTime); 100 mBatteryDischargeSamples++; 101 mBatteryDischarge.addBatteryDischargeInfo( 102 getDischargeClockTime(days, hours, mins, secs), 103 (elapsedTime - previousDischargeElapsedTime), batteryLevel); 104 previousDischargeElapsedTime = elapsedTime; 105 previousBatteryLevel = batteryLevel; 106 } 107 } 108 } 109 mItem.setBatteryDischargeRate(getAverageDischargeRate()); 110 mItem.setPeakDischargeTime(getPeakDischargeTime()); 111 return mItem; 112 } 113 getDischargeClockTime(int days, int hours, int mins, int secs)114 private Calendar getDischargeClockTime(int days, int hours, int mins, int secs) { 115 Calendar dischargeClockTime = new GregorianCalendar(); 116 117 dischargeClockTime.setTime(mResetTime.getTime()); 118 dischargeClockTime.add(Calendar.DATE, days); 119 dischargeClockTime.add(Calendar.HOUR, hours); 120 dischargeClockTime.add(Calendar.MINUTE, mins); 121 dischargeClockTime.add(Calendar.SECOND, secs); 122 return dischargeClockTime; 123 } 124 getAverageDischargeRate()125 private String getAverageDischargeRate() { 126 if (mBatteryDischargeSamples == 0) { 127 return "The battery did not discharge"; 128 } 129 130 final long minsPerLevel = mBatteryDischargeRateAvg / (mBatteryDischargeSamples * 60 * 1000); 131 return String.format("The battery dropped a level %d mins on average", minsPerLevel); 132 } 133 getPeakDischargeTime()134 private String getPeakDischargeTime() { 135 136 int peakDischargeStartBatteryLevel = 0, peakDischargeStopBatteryLevel = 0; 137 long minDischargeDuration = 0; 138 Calendar peakDischargeStartTime= null, peakDischargeStopTime = null; 139 Queue <BatteryDischargeInfoItem> batteryDischargeWindow = 140 new LinkedList <BatteryDischargeInfoItem>(); 141 long sumDischargeDuration = 0; 142 for (BatteryDischargeInfoItem dischargeSteps : mBatteryDischarge.getDischargeStepsInfo()) { 143 batteryDischargeWindow.add(dischargeSteps); 144 sumDischargeDuration += dischargeSteps.getElapsedTime(); 145 if (batteryDischargeWindow.size() >= BATTERY_GROUP_LIMIT) { 146 final long averageDischargeDuration = sumDischargeDuration/BATTERY_GROUP_LIMIT; 147 final BatteryDischargeInfoItem startNode = batteryDischargeWindow.remove(); 148 sumDischargeDuration -= startNode.getElapsedTime(); 149 150 if (minDischargeDuration == 0 || averageDischargeDuration < minDischargeDuration) { 151 minDischargeDuration = averageDischargeDuration; 152 peakDischargeStartBatteryLevel = startNode.getBatteryLevel(); 153 peakDischargeStopBatteryLevel = dischargeSteps.getBatteryLevel(); 154 peakDischargeStartTime = startNode.getClockTime(); 155 peakDischargeStopTime = dischargeSteps.getClockTime(); 156 } 157 } 158 } 159 if (peakDischargeStartTime != null && peakDischargeStopTime != null && 160 peakDischargeStartBatteryLevel > 0 && peakDischargeStopBatteryLevel > 0) { 161 return String.format( 162 "The peak discharge time was during %s to %s where battery dropped from %d to " 163 + "%d", peakDischargeStartTime.getTime().toString(), 164 peakDischargeStopTime.getTime().toString(), peakDischargeStartBatteryLevel, 165 peakDischargeStopBatteryLevel); 166 167 } else { 168 return "The battery did not discharge"; 169 } 170 } 171 172 /** 173 * Get the {@link BatteryStatsSummaryInfoItem}. 174 * <p> 175 * Exposed for unit testing. 176 * </p> 177 */ getItem()178 BatteryStatsSummaryInfoItem getItem() { 179 return mItem; 180 } 181 } 182