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.TimeSparseArray; 20 import android.app.usage.UsageEvents; 21 import android.app.usage.UsageStats; 22 import android.content.res.Configuration; 23 import android.util.ArrayMap; 24 import android.util.ArraySet; 25 26 class IntervalStats { 27 public long beginTime; 28 public long endTime; 29 public long lastTimeSaved; 30 public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>(); 31 public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>(); 32 public Configuration activeConfiguration; 33 public TimeSparseArray<UsageEvents.Event> events; 34 35 // A string cache. This is important as when we're parsing XML files, we don't want to 36 // keep hundreds of strings that have the same contents. We will read the string 37 // and only keep it if it's not in the cache. The GC will take care of the 38 // strings that had identical copies in the cache. 39 private final ArraySet<String> mStringCache = new ArraySet<>(); 40 41 /** 42 * Gets the UsageStats object for the given package, or creates one and adds it internally. 43 */ getOrCreateUsageStats(String packageName)44 UsageStats getOrCreateUsageStats(String packageName) { 45 UsageStats usageStats = packageStats.get(packageName); 46 if (usageStats == null) { 47 usageStats = new UsageStats(); 48 usageStats.mPackageName = getCachedStringRef(packageName); 49 usageStats.mBeginTimeStamp = beginTime; 50 usageStats.mEndTimeStamp = endTime; 51 packageStats.put(usageStats.mPackageName, usageStats); 52 } 53 return usageStats; 54 } 55 56 /** 57 * Gets the ConfigurationStats object for the given configuration, or creates one and adds it 58 * internally. 59 */ getOrCreateConfigurationStats(Configuration config)60 ConfigurationStats getOrCreateConfigurationStats(Configuration config) { 61 ConfigurationStats configStats = configurations.get(config); 62 if (configStats == null) { 63 configStats = new ConfigurationStats(); 64 configStats.mBeginTimeStamp = beginTime; 65 configStats.mEndTimeStamp = endTime; 66 configStats.mConfiguration = config; 67 configurations.put(config, configStats); 68 } 69 return configStats; 70 } 71 72 /** 73 * Builds a UsageEvents.Event, but does not add it internally. 74 */ buildEvent(String packageName, String className)75 UsageEvents.Event buildEvent(String packageName, String className) { 76 UsageEvents.Event event = new UsageEvents.Event(); 77 event.mPackage = getCachedStringRef(packageName); 78 if (className != null) { 79 event.mClass = getCachedStringRef(className); 80 } 81 return event; 82 } 83 isStatefulEvent(int eventType)84 private boolean isStatefulEvent(int eventType) { 85 switch (eventType) { 86 case UsageEvents.Event.MOVE_TO_FOREGROUND: 87 case UsageEvents.Event.MOVE_TO_BACKGROUND: 88 case UsageEvents.Event.END_OF_DAY: 89 case UsageEvents.Event.CONTINUE_PREVIOUS_DAY: 90 return true; 91 } 92 return false; 93 } 94 update(String packageName, long timeStamp, int eventType)95 void update(String packageName, long timeStamp, int eventType) { 96 UsageStats usageStats = getOrCreateUsageStats(packageName); 97 98 // TODO(adamlesinski): Ensure that we recover from incorrect event sequences 99 // like double MOVE_TO_BACKGROUND, etc. 100 if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND || 101 eventType == UsageEvents.Event.END_OF_DAY) { 102 if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND || 103 usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) { 104 usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed; 105 } 106 } 107 108 if (isStatefulEvent(eventType)) { 109 usageStats.mLastEvent = eventType; 110 } 111 112 if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) { 113 usageStats.mLastTimeUsed = timeStamp; 114 } 115 usageStats.mEndTimeStamp = timeStamp; 116 117 if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) { 118 usageStats.mLaunchCount += 1; 119 } 120 121 endTime = timeStamp; 122 } 123 updateChooserCounts(String packageName, String category, String action)124 void updateChooserCounts(String packageName, String category, String action) { 125 UsageStats usageStats = getOrCreateUsageStats(packageName); 126 if (usageStats.mChooserCounts == null) { 127 usageStats.mChooserCounts = new ArrayMap<>(); 128 } 129 ArrayMap<String, Integer> chooserCounts; 130 final int idx = usageStats.mChooserCounts.indexOfKey(action); 131 if (idx < 0) { 132 chooserCounts = new ArrayMap<>(); 133 usageStats.mChooserCounts.put(action, chooserCounts); 134 } else { 135 chooserCounts = usageStats.mChooserCounts.valueAt(idx); 136 } 137 int currentCount = chooserCounts.getOrDefault(category, 0); 138 chooserCounts.put(category, currentCount + 1); 139 } 140 updateConfigurationStats(Configuration config, long timeStamp)141 void updateConfigurationStats(Configuration config, long timeStamp) { 142 if (activeConfiguration != null) { 143 ConfigurationStats activeStats = configurations.get(activeConfiguration); 144 activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive; 145 activeStats.mLastTimeActive = timeStamp - 1; 146 } 147 148 if (config != null) { 149 ConfigurationStats configStats = getOrCreateConfigurationStats(config); 150 configStats.mLastTimeActive = timeStamp; 151 configStats.mActivationCount += 1; 152 activeConfiguration = configStats.mConfiguration; 153 } 154 155 endTime = timeStamp; 156 } 157 getCachedStringRef(String str)158 private String getCachedStringRef(String str) { 159 final int index = mStringCache.indexOf(str); 160 if (index < 0) { 161 mStringCache.add(str); 162 return str; 163 } 164 return mStringCache.valueAt(index); 165 } 166 } 167