1 /*
2  * Copyright (C) 2016 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.IItem;
20 import com.android.loganalysis.item.LatencyItem;
21 import com.android.loganalysis.item.TransitionDelayItem;
22 
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 
32 /**
33  * Parse the events logs. </p>
34  */
35 public class EventsLogParser implements IParser {
36 
37     // 09-18 23:56:19.376 1140 1221 I sysui_multi_action:
38     // [319,51,321,50,322,190,325,670,757,761,758,7,759,1,806,com.google.android.calculator,871,
39     // com.android.calculator2.Calculator,905,0,945,41]
40     private static final Pattern SYSUI_TRANSITION_INFO_PATTERN = Pattern.compile(
41             "^(?<date>[0-9-]*)\\s+(?<time>[0-9:.]*)\\s+\\d+\\s+\\d+ I sysui_multi_action:"
42                     + " \\[(?<transitioninfo>.*)\\]$");
43 
44     // 08-21 17:53:53.876 1053 2135 I sysui_latency: [1,50]
45     private static final Pattern ACTION_LATENCY = Pattern.compile("^(?<date>[0-9-]*)\\s+"
46             + "(?<time>[0-9:.]*)\\s+\\d+\\s+\\d+ I sysui_latency: \\[(?<action>.*),"
47             + "(?<delay>.*)\\]$");
48 
49     private static final String DATE = "date";
50     private static final String TIME = "time";
51     private static final String TRANSITION_INFO = "transitioninfo";
52     private static final String PACKAGE_KEY = "806";
53     private static final String ACTIVITY_KEY = "871";
54     private static final String TRANSITION_DELAY_KEY = "319";
55     private static final String STARTING_WINDOW_DELAY_KEY = "321";
56     private static final String COLD_LAUNCH_KEY = "945";
57     private static final String WINDOWS_DRAWN_DELAY_KEY = "322";
58 
59     @Override
parse(List<String> lines)60     public IItem parse(List<String> lines) {
61         throw new UnsupportedOperationException("Method has not been implemented in lieu"
62                 + " of others");
63     }
64 
65     /**
66      * Parse the transition delay information from the events log.
67      * @param input
68      * @return list of transition delay items.
69      * @throws IOException
70      */
parseTransitionDelayInfo(BufferedReader input)71     public List<TransitionDelayItem> parseTransitionDelayInfo(BufferedReader input)
72             throws IOException {
73         List<TransitionDelayItem> transitionDelayItems = new ArrayList<TransitionDelayItem>();
74         String line;
75         Matcher match = null;
76         while ((line = input.readLine()) != null) {
77             if ((match = matches(SYSUI_TRANSITION_INFO_PATTERN, line)) != null) {
78                 Map<String, String> transitionInfoMap = getTransitionInfoMap(
79                         match.group(TRANSITION_INFO));
80                 if (transitionInfoMap.containsKey(TRANSITION_DELAY_KEY)) {
81                     TransitionDelayItem delayItem = new TransitionDelayItem();
82                     if (null != transitionInfoMap.get(PACKAGE_KEY)
83                             && null != transitionInfoMap.get(ACTIVITY_KEY)
84                             && null != transitionInfoMap.get(TRANSITION_DELAY_KEY)
85                             && null != transitionInfoMap.get(WINDOWS_DRAWN_DELAY_KEY)) {
86                         delayItem.setComponentName(transitionInfoMap.get(PACKAGE_KEY) + "/"
87                                 + transitionInfoMap.get(ACTIVITY_KEY));
88                         delayItem.setTransitionDelay(Long.parseLong(transitionInfoMap
89                                 .get(TRANSITION_DELAY_KEY)));
90                         delayItem.setDateTime(String.format("%s %s", match.group(DATE),
91                                 match.group(TIME)));
92                         delayItem.setWindowDrawnDelay(
93                                 Long.parseLong(transitionInfoMap.get(WINDOWS_DRAWN_DELAY_KEY)));
94                     }
95                     if (transitionInfoMap.containsKey(COLD_LAUNCH_KEY)) {
96                         if (null != transitionInfoMap.get(STARTING_WINDOW_DELAY_KEY)) {
97                             delayItem.setStartingWindowDelay(Long.parseLong(transitionInfoMap
98                                     .get(STARTING_WINDOW_DELAY_KEY)));
99                         }
100                     }
101                     transitionDelayItems.add(delayItem);
102                 }
103             }
104         }
105         return transitionDelayItems;
106     }
107 
108     /**
109      * Split the transition info string in to key, values and return a map.
110      * @param transitionInfo transition info map in hey value format.
111      * @return
112      */
getTransitionInfoMap(String transitionInfo)113     public Map<String, String> getTransitionInfoMap(String transitionInfo) {
114         String[] transitionSplit = transitionInfo.split(",");
115         Map<String, String> transitionInfoMap = new HashMap<>();
116         if (transitionSplit.length % 2 == 0) {
117             for (int i = 0; i < transitionSplit.length; i = i + 2) {
118                 transitionInfoMap.put(transitionSplit[i], transitionSplit[i + 1]);
119             }
120         }
121         return transitionInfoMap;
122     }
123 
124     /**
125      * Method to parse the latency information from the events log
126      * @param input
127      * @return
128      * @throws IOException
129      */
parseLatencyInfo(BufferedReader input)130     public List<LatencyItem> parseLatencyInfo(BufferedReader input) throws IOException {
131         List<LatencyItem> latencyItems = new ArrayList<LatencyItem>();
132         String line;
133         while ((line = input.readLine()) != null) {
134             Matcher match = null;
135             if ((match = matches(ACTION_LATENCY, line)) != null) {
136                 LatencyItem latencyItem = new LatencyItem();
137                 latencyItem.setActionId(Integer.parseInt(match.group("action")));
138                 latencyItem.setDelay(Long.parseLong(match.group("delay")));
139                 latencyItems.add(latencyItem);
140             }
141         }
142         return latencyItems;
143     }
144 
145     /**
146      * Checks whether {@code line} matches the given {@link Pattern}.
147      *
148      * @return The resulting {@link Matcher} obtained by matching the {@code line} against
149      *         {@code pattern}, or null if the {@code line} does not match.
150      */
matches(Pattern pattern, String line)151     private static Matcher matches(Pattern pattern, String line) {
152         Matcher ret = pattern.matcher(line);
153         return ret.matches() ? ret : null;
154     }
155 
156 }
157