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 
17 package com.android.loganalysis.parser;
18 
19 import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
20 import com.android.loganalysis.item.BatteryUsageItem;
21 import com.android.loganalysis.item.InterruptItem;
22 import com.android.loganalysis.item.ProcessUsageItem;
23 import com.android.loganalysis.item.WakelockItem;
24 import com.android.loganalysis.util.NumberFormattingUtil;
25 
26 import java.util.List;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29 
30 
31 /**
32  * A {@link IParser} to parse the battery stats section of the bugreport
33  */
34 public class BatteryStatsDetailedInfoParser extends AbstractSectionParser {
35 
36     private static final String BATTERY_USAGE_SECTION_REGEX = "^\\s*Estimated power use \\(mAh\\):$";
37     private static final String KERNEL_WAKELOCK_SECTION_REGEX = "^\\s*All kernel wake locks:$";
38     private static final String PARTIAL_WAKELOCK_SECTION_REGEX = "^\\s*All partial wake locks:$";
39     private static final String INTERRUPT_SECTION_REGEX = "^\\s*All wakeup reasons:$";
40     private static final String PROCESS_USAGE_SECTION_REGEX = "^\\s*0:$";
41 
42     /**
43      * Matches: Time on battery: 7h 45m 54s 332ms (98.3%) realtime, 4h 40m 51s 315ms (59.3%) uptime
44      */
45     private static final Pattern TIME_ON_BATTERY_PATTERN = Pattern.compile(
46             "^\\s*Time on battery: (?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?" +
47             "\\s?(?:(\\d+)ms)?.*");
48     /**
49      * Matches:Time on battery screen off: 1d 4h 6m 16s 46ms (99.1%) realtime, 6h 37m 49s 201ms
50      */
51     private static final Pattern SCREEN_OFF_TIME_PATTERN = Pattern.compile("^\\s*Time on battery "
52         + "screen off: (?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?\\s?"
53         + "(?:(\\d+)ms).*");
54 
55     private BatteryUsageParser mBatteryUsageParser = new BatteryUsageParser();
56     private WakelockParser mWakelockParser = new WakelockParser();
57     private InterruptParser mInterruptParser = new InterruptParser();
58     private ProcessUsageParser mProcessUsageParser = new ProcessUsageParser();
59 
60     private IParser mBatteryTimeParser = new IParser() {
61         @Override
62         public BatteryStatsDetailedInfoItem parse(List<String> lines) {
63             BatteryStatsDetailedInfoItem detailedInfo = null;
64             long timeOnBattery = 0, screenOffTime = 0;
65             Matcher m = null;
66             for (String line : lines) {
67                 if (detailedInfo == null && !"".equals(line.trim())) {
68                     detailedInfo = new BatteryStatsDetailedInfoItem();
69                 }
70                 m = TIME_ON_BATTERY_PATTERN.matcher(line);
71                 if (m.matches()) {
72                     timeOnBattery = NumberFormattingUtil.getMs(
73                             NumberFormattingUtil.parseIntOrZero(m.group(1)),
74                             NumberFormattingUtil.parseIntOrZero(m.group(2)),
75                             NumberFormattingUtil.parseIntOrZero(m.group(3)),
76                             NumberFormattingUtil.parseIntOrZero(m.group(4)),
77                             NumberFormattingUtil.parseIntOrZero(m.group(5)));
78                     detailedInfo.setTimeOnBattery(timeOnBattery);
79                 } else {
80                     m = SCREEN_OFF_TIME_PATTERN.matcher(line);
81                     if (m.matches()) {
82                         screenOffTime = NumberFormattingUtil.getMs(
83                                 NumberFormattingUtil.parseIntOrZero(m.group(1)),
84                                 NumberFormattingUtil.parseIntOrZero(m.group(2)),
85                                 NumberFormattingUtil.parseIntOrZero(m.group(3)),
86                                 NumberFormattingUtil.parseIntOrZero(m.group(4)),
87                                 NumberFormattingUtil.parseIntOrZero(m.group(5)));
88                         detailedInfo.setScreenOnTime(getScreenOnTime(timeOnBattery, screenOffTime));
89                         return detailedInfo;
90                     }
91                 }
92             }
93             return detailedInfo;
94         }
95 
96         private long getScreenOnTime(long timeOnBattery, long screenOffTime) {
97             if (timeOnBattery > screenOffTime) {
98                 return (timeOnBattery - screenOffTime);
99             }
100             return 0;
101         }
102     };
103 
104     private BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfoItem = null;
105     private boolean mParsedInput = false;
106 
107     /**
108      * {@inheritDoc}
109      *
110      * @return The {@link BatteryStatsDetailedInfoItem}
111      */
112     @Override
parse(List<String> lines)113     public BatteryStatsDetailedInfoItem parse(List<String> lines) {
114         setup();
115         for (String line : lines) {
116             if (!mParsedInput && !"".equals(line.trim())) {
117                 mParsedInput = true;
118             }
119             parseLine(line);
120         }
121         commit();
122         return mBatteryStatsDetailedInfoItem;
123     }
124 
125     /**
126      * Sets up the parser by adding the section parsers.
127      */
setup()128     protected void setup() {
129         setParser(mBatteryTimeParser);
130         addSectionParser(mBatteryUsageParser, BATTERY_USAGE_SECTION_REGEX);
131         addSectionParser(mWakelockParser, KERNEL_WAKELOCK_SECTION_REGEX);
132         addSectionParser(mWakelockParser, PARTIAL_WAKELOCK_SECTION_REGEX);
133         addSectionParser(mInterruptParser, INTERRUPT_SECTION_REGEX);
134         addSectionParser(mProcessUsageParser, PROCESS_USAGE_SECTION_REGEX);
135     }
136 
137     /**
138      * Set the {@link BatteryStatsDetailedInfoItem}
139      *
140      */
141     @Override
onSwitchParser()142     protected void onSwitchParser() {
143         if (mBatteryStatsDetailedInfoItem == null) {
144             mBatteryStatsDetailedInfoItem = (BatteryStatsDetailedInfoItem)
145                     getSection(mBatteryTimeParser);
146         }
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     @Override
commit()153     protected void commit() {
154         // signal EOF
155         super.commit();
156         if (mParsedInput) {
157             if (mBatteryStatsDetailedInfoItem == null) {
158                 mBatteryStatsDetailedInfoItem = new BatteryStatsDetailedInfoItem();
159             }
160         }
161 
162         if (mBatteryStatsDetailedInfoItem != null) {
163             mBatteryStatsDetailedInfoItem.setBatteryUsageItem(
164                     (BatteryUsageItem) getSection(mBatteryUsageParser));
165             mBatteryStatsDetailedInfoItem.setWakelockItem(
166                     (WakelockItem) getSection(mWakelockParser));
167             mBatteryStatsDetailedInfoItem.setInterruptItem(
168                     (InterruptItem) getSection(mInterruptParser));
169             mBatteryStatsDetailedInfoItem.setProcessUsageItem(
170                     (ProcessUsageItem) getSection(mProcessUsageParser));
171         }
172     }
173 }
174