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 55 public void commitTime(long timeStamp) { 56 if (curStartTime != 0) { 57 duration += timeStamp - duration; 58 curStartTime = 0; 59 } 60 } 61 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 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 */ 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 */ 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 */ 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 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 */ 147 private boolean isUserVisibleEvent(int eventType) { 148 return eventType != UsageEvents.Event.SYSTEM_INTERACTION 149 && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED; 150 } 151 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 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 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 215 void incrementAppLaunchCount(String packageName) { 216 UsageStats usageStats = getOrCreateUsageStats(packageName); 217 usageStats.mAppLaunchCount += 1; 218 } 219 220 void commitTime(long timeStamp) { 221 interactiveTracker.commitTime(timeStamp); 222 nonInteractiveTracker.commitTime(timeStamp); 223 keyguardShownTracker.commitTime(timeStamp); 224 keyguardHiddenTracker.commitTime(timeStamp); 225 } 226 227 void updateScreenInteractive(long timeStamp) { 228 interactiveTracker.update(timeStamp); 229 nonInteractiveTracker.commitTime(timeStamp); 230 } 231 232 void updateScreenNonInteractive(long timeStamp) { 233 nonInteractiveTracker.update(timeStamp); 234 interactiveTracker.commitTime(timeStamp); 235 } 236 237 void updateKeyguardShown(long timeStamp) { 238 keyguardShownTracker.update(timeStamp); 239 keyguardHiddenTracker.commitTime(timeStamp); 240 } 241 242 void updateKeyguardHidden(long timeStamp) { 243 keyguardHiddenTracker.update(timeStamp); 244 keyguardShownTracker.commitTime(timeStamp); 245 } 246 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 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