• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 package com.android.server.usage;
17 
18 import android.app.usage.ConfigurationStats;
19 import android.app.usage.EventList;
20 import android.app.usage.EventStats;
21 import android.app.usage.TimeSparseArray;
22 import android.app.usage.UsageEvents;
23 import android.app.usage.UsageStats;
24 import android.content.res.Configuration;
25 import android.util.ArrayMap;
26 import android.util.ArraySet;
27 
28 import java.util.List;
29 
30 class IntervalStats {
31     public long beginTime;
32     public long endTime;
33     public long lastTimeSaved;
34     public final EventTracker interactiveTracker = new EventTracker();
35     public final EventTracker nonInteractiveTracker = new EventTracker();
36     public final EventTracker keyguardShownTracker = new EventTracker();
37     public final EventTracker keyguardHiddenTracker = new EventTracker();
38     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
39     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
40     public Configuration activeConfiguration;
41     public EventList events;
42 
43     // A string cache. This is important as when we're parsing XML files, we don't want to
44     // keep hundreds of strings that have the same contents. We will read the string
45     // and only keep it if it's not in the cache. The GC will take care of the
46     // strings that had identical copies in the cache.
47     private final ArraySet<String> mStringCache = new ArraySet<>();
48 
49     public static final class EventTracker {
50         public long curStartTime;
51         public long lastEventTime;
52         public long duration;
53         public int count;
54 
commitTime(long timeStamp)55         public void commitTime(long timeStamp) {
56             if (curStartTime != 0) {
57                 duration += timeStamp - duration;
58                 curStartTime = 0;
59             }
60         }
61 
update(long timeStamp)62         public void update(long timeStamp) {
63             if (curStartTime == 0) {
64                 // If we aren't already running, time to bump the count.
65                 count++;
66             }
67             commitTime(timeStamp);
68             curStartTime = timeStamp;
69             lastEventTime = timeStamp;
70         }
71 
addToEventStats(List<EventStats> out, int event, long beginTime, long endTime)72         void addToEventStats(List<EventStats> out, int event, long beginTime, long endTime) {
73             if (count != 0 || duration != 0) {
74                 EventStats ev = new EventStats();
75                 ev.mEventType = event;
76                 ev.mCount = count;
77                 ev.mTotalTime = duration;
78                 ev.mLastEventTime = lastEventTime;
79                 ev.mBeginTimeStamp = beginTime;
80                 ev.mEndTimeStamp = endTime;
81                 out.add(ev);
82             }
83         }
84 
85     }
86 
87     /**
88      * Gets the UsageStats object for the given package, or creates one and adds it internally.
89      */
getOrCreateUsageStats(String packageName)90     UsageStats getOrCreateUsageStats(String packageName) {
91         UsageStats usageStats = packageStats.get(packageName);
92         if (usageStats == null) {
93             usageStats = new UsageStats();
94             usageStats.mPackageName = getCachedStringRef(packageName);
95             usageStats.mBeginTimeStamp = beginTime;
96             usageStats.mEndTimeStamp = endTime;
97             packageStats.put(usageStats.mPackageName, usageStats);
98         }
99         return usageStats;
100     }
101 
102     /**
103      * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
104      * internally.
105      */
getOrCreateConfigurationStats(Configuration config)106     ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
107         ConfigurationStats configStats = configurations.get(config);
108         if (configStats == null) {
109             configStats = new ConfigurationStats();
110             configStats.mBeginTimeStamp = beginTime;
111             configStats.mEndTimeStamp = endTime;
112             configStats.mConfiguration = config;
113             configurations.put(config, configStats);
114         }
115         return configStats;
116     }
117 
118     /**
119      * Builds a UsageEvents.Event, but does not add it internally.
120      */
buildEvent(String packageName, String className)121     UsageEvents.Event buildEvent(String packageName, String className) {
122         UsageEvents.Event event = new UsageEvents.Event();
123         event.mPackage = getCachedStringRef(packageName);
124         if (className != null) {
125             event.mClass = getCachedStringRef(className);
126         }
127         return event;
128     }
129 
isStatefulEvent(int eventType)130     private boolean isStatefulEvent(int eventType) {
131         switch (eventType) {
132             case UsageEvents.Event.MOVE_TO_FOREGROUND:
133             case UsageEvents.Event.MOVE_TO_BACKGROUND:
134             case UsageEvents.Event.END_OF_DAY:
135             case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
136                 return true;
137         }
138         return false;
139     }
140 
141     /**
142      * Returns whether the event type is one caused by user visible
143      * interaction. Excludes those that are internally generated.
144      * @param eventType
145      * @return
146      */
isUserVisibleEvent(int eventType)147     private boolean isUserVisibleEvent(int eventType) {
148         return eventType != UsageEvents.Event.SYSTEM_INTERACTION
149                 && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
150     }
151 
update(String packageName, long timeStamp, int eventType)152     void update(String packageName, long timeStamp, int eventType) {
153         UsageStats usageStats = getOrCreateUsageStats(packageName);
154 
155         // TODO(adamlesinski): Ensure that we recover from incorrect event sequences
156         // like double MOVE_TO_BACKGROUND, etc.
157         if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND ||
158                 eventType == UsageEvents.Event.END_OF_DAY) {
159             if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
160                     usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
161                 usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed;
162             }
163         }
164 
165         if (isStatefulEvent(eventType)) {
166             usageStats.mLastEvent = eventType;
167         }
168 
169         if (isUserVisibleEvent(eventType)) {
170             usageStats.mLastTimeUsed = timeStamp;
171         }
172         usageStats.mEndTimeStamp = timeStamp;
173 
174         if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
175             usageStats.mLaunchCount += 1;
176         }
177 
178         endTime = timeStamp;
179     }
180 
updateChooserCounts(String packageName, String category, String action)181     void updateChooserCounts(String packageName, String category, String action) {
182         UsageStats usageStats = getOrCreateUsageStats(packageName);
183         if (usageStats.mChooserCounts == null) {
184             usageStats.mChooserCounts = new ArrayMap<>();
185         }
186         ArrayMap<String, Integer> chooserCounts;
187         final int idx = usageStats.mChooserCounts.indexOfKey(action);
188         if (idx < 0) {
189             chooserCounts = new ArrayMap<>();
190             usageStats.mChooserCounts.put(action, chooserCounts);
191         } else {
192             chooserCounts = usageStats.mChooserCounts.valueAt(idx);
193         }
194         int currentCount = chooserCounts.getOrDefault(category, 0);
195         chooserCounts.put(category, currentCount + 1);
196     }
197 
updateConfigurationStats(Configuration config, long timeStamp)198     void updateConfigurationStats(Configuration config, long timeStamp) {
199         if (activeConfiguration != null) {
200             ConfigurationStats activeStats = configurations.get(activeConfiguration);
201             activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
202             activeStats.mLastTimeActive = timeStamp - 1;
203         }
204 
205         if (config != null) {
206             ConfigurationStats configStats = getOrCreateConfigurationStats(config);
207             configStats.mLastTimeActive = timeStamp;
208             configStats.mActivationCount += 1;
209             activeConfiguration = configStats.mConfiguration;
210         }
211 
212         endTime = timeStamp;
213     }
214 
incrementAppLaunchCount(String packageName)215     void incrementAppLaunchCount(String packageName) {
216         UsageStats usageStats = getOrCreateUsageStats(packageName);
217         usageStats.mAppLaunchCount += 1;
218     }
219 
commitTime(long timeStamp)220     void commitTime(long timeStamp) {
221         interactiveTracker.commitTime(timeStamp);
222         nonInteractiveTracker.commitTime(timeStamp);
223         keyguardShownTracker.commitTime(timeStamp);
224         keyguardHiddenTracker.commitTime(timeStamp);
225     }
226 
updateScreenInteractive(long timeStamp)227     void updateScreenInteractive(long timeStamp) {
228         interactiveTracker.update(timeStamp);
229         nonInteractiveTracker.commitTime(timeStamp);
230     }
231 
updateScreenNonInteractive(long timeStamp)232     void updateScreenNonInteractive(long timeStamp) {
233         nonInteractiveTracker.update(timeStamp);
234         interactiveTracker.commitTime(timeStamp);
235     }
236 
updateKeyguardShown(long timeStamp)237     void updateKeyguardShown(long timeStamp) {
238         keyguardShownTracker.update(timeStamp);
239         keyguardHiddenTracker.commitTime(timeStamp);
240     }
241 
updateKeyguardHidden(long timeStamp)242     void updateKeyguardHidden(long timeStamp) {
243         keyguardHiddenTracker.update(timeStamp);
244         keyguardShownTracker.commitTime(timeStamp);
245     }
246 
addEventStatsTo(List<EventStats> out)247     void addEventStatsTo(List<EventStats> out) {
248         interactiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE,
249                 beginTime, endTime);
250         nonInteractiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE,
251                 beginTime, endTime);
252         keyguardShownTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_SHOWN,
253                 beginTime, endTime);
254         keyguardHiddenTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_HIDDEN,
255                 beginTime, endTime);
256     }
257 
getCachedStringRef(String str)258     private String getCachedStringRef(String str) {
259         final int index = mStringCache.indexOf(str);
260         if (index < 0) {
261             mStringCache.add(str);
262             return str;
263         }
264         return mStringCache.valueAt(index);
265     }
266 }
267