summaryrefslogtreecommitdiffstats
path: root/services/usage/java/com/android/server
diff options
context:
space:
mode:
authorAdam Lesinski <adamlesinski@google.com>2014-09-02 16:43:52 -0700
committerAdam Lesinski <adamlesinski@google.com>2014-09-04 12:15:32 -0700
commit7f61e96db7c90c1f4418359672aa4656aebee500 (patch)
tree6748f94db635e24c26a6a7236c444e2c0d906181 /services/usage/java/com/android/server
parentd400ca2f8db42e57e41f2a999833703619348fef (diff)
downloadframeworks_base-7f61e96db7c90c1f4418359672aa4656aebee500.zip
frameworks_base-7f61e96db7c90c1f4418359672aa4656aebee500.tar.gz
frameworks_base-7f61e96db7c90c1f4418359672aa4656aebee500.tar.bz2
Add Configuration changes to UsageStats
Bug:17354208 Change-Id: I9b2f595e51b656607e30e798926cfb7e25134944
Diffstat (limited to 'services/usage/java/com/android/server')
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java72
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java34
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java75
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsXmlV1.java160
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java110
5 files changed, 346 insertions, 105 deletions
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index dc036e2..6c80a65 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,17 +15,23 @@
*/
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
+import android.content.res.Configuration;
import android.util.ArrayMap;
import android.util.ArraySet;
+import java.util.ArrayList;
+
class IntervalStats {
public long beginTime;
public long endTime;
public long lastTimeSaved;
public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
+ public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
+ public Configuration activeConfiguration;
public TimeSparseArray<UsageEvents.Event> events;
// A string cache. This is important as when we're parsing XML files, we don't want to
@@ -34,18 +40,49 @@ class IntervalStats {
// strings that had identical copies in the cache.
private final ArraySet<String> mStringCache = new ArraySet<>();
+ /**
+ * Gets the UsageStats object for the given package, or creates one and adds it internally.
+ */
UsageStats getOrCreateUsageStats(String packageName) {
UsageStats usageStats = stats.get(packageName);
if (usageStats == null) {
usageStats = new UsageStats();
- usageStats.mPackageName = packageName;
+ usageStats.mPackageName = getCachedStringRef(packageName);
usageStats.mBeginTimeStamp = beginTime;
usageStats.mEndTimeStamp = endTime;
- stats.put(packageName, usageStats);
+ stats.put(usageStats.mPackageName, usageStats);
}
return usageStats;
}
+ /**
+ * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
+ * internally.
+ */
+ ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
+ ConfigurationStats configStats = configurations.get(config);
+ if (configStats == null) {
+ configStats = new ConfigurationStats();
+ configStats.mBeginTimeStamp = beginTime;
+ configStats.mEndTimeStamp = endTime;
+ configStats.mConfiguration = config;
+ configurations.put(config, configStats);
+ }
+ return configStats;
+ }
+
+ /**
+ * Builds a UsageEvents.Event, but does not add it internally.
+ */
+ UsageEvents.Event buildEvent(String packageName, String className) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = getCachedStringRef(packageName);
+ if (className != null) {
+ event.mClass = getCachedStringRef(className);
+ }
+ return event;
+ }
+
void update(String packageName, long timeStamp, int eventType) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
@@ -61,6 +98,28 @@ class IntervalStats {
usageStats.mLastEvent = eventType;
usageStats.mLastTimeUsed = timeStamp;
usageStats.mEndTimeStamp = timeStamp;
+
+ if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ usageStats.mLaunchCount += 1;
+ }
+
+ endTime = timeStamp;
+ }
+
+ void updateConfigurationStats(Configuration config, long timeStamp) {
+ if (activeConfiguration != null) {
+ ConfigurationStats activeStats = configurations.get(activeConfiguration);
+ activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
+ activeStats.mLastTimeActive = timeStamp - 1;
+ }
+
+ if (config != null) {
+ ConfigurationStats configStats = getOrCreateConfigurationStats(config);
+ configStats.mLastTimeActive = timeStamp;
+ configStats.mActivationCount += 1;
+ activeConfiguration = configStats.mConfiguration;
+ }
+
endTime = timeStamp;
}
@@ -72,13 +131,4 @@ class IntervalStats {
}
return mStringCache.valueAt(index);
}
-
- UsageEvents.Event buildEvent(String packageName, String className) {
- UsageEvents.Event event = new UsageEvents.Event();
- event.mPackage = getCachedStringRef(packageName);
- if (className != null) {
- event.mClass = getCachedStringRef(className);
- }
- return event;
- }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index e6ce0fe..37340a4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,7 +17,6 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
-import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.util.AtomicFile;
import android.util.Slog;
@@ -133,9 +132,30 @@ class UsageStatsDatabase {
}
/**
- * Find all {@link UsageStats} for the given range and interval type.
+ * Figures out what to extract from the given IntervalStats object.
*/
- public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
+ interface StatCombiner<T> {
+
+ /**
+ * Implementations should extract interesting from <code>stats</code> and add it
+ * to the <code>accumulatedResult</code> list.
+ *
+ * If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
+ * which means you should make a copy of the data before adding it to the
+ * <code>accumulatedResult</code> list.
+ *
+ * @param stats The {@link IntervalStats} object selected.
+ * @param mutable Whether or not the data inside the stats object is mutable.
+ * @param accumulatedResult The list to which to add extracted data.
+ */
+ void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
+ }
+
+ /**
+ * Find all {@link IntervalStats} for the given range and interval type.
+ */
+ public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
+ StatCombiner<T> combiner) {
synchronized (mLock) {
if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
throw new IllegalArgumentException("Bad interval type " + intervalType);
@@ -157,7 +177,7 @@ class UsageStatsDatabase {
try {
IntervalStats stats = new IntervalStats();
- ArrayList<UsageStats> results = new ArrayList<>();
+ ArrayList<T> results = new ArrayList<>();
for (int i = startIndex; i <= endIndex; i++) {
final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
@@ -167,7 +187,7 @@ class UsageStatsDatabase {
UsageStatsXml.read(f, stats);
if (beginTime < stats.endTime) {
- results.addAll(stats.stats.values());
+ combiner.combine(stats, false, results);
}
}
return results;
@@ -209,6 +229,10 @@ class UsageStatsDatabase {
public void prune() {
synchronized (mLock) {
long timeNow = System.currentTimeMillis();
+ mCal.setTimeInMillis(timeNow);
+ mCal.add(Calendar.YEAR, -3);
+ pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
+ mCal.getTimeInMillis());
mCal.setTimeInMillis(timeNow);
mCal.add(Calendar.MONTH, -6);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0e8b427..e77bf86 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,6 +18,7 @@ package com.android.server.usage;
import android.Manifest;
import android.app.AppOpsManager;
+import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
@@ -30,11 +31,13 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
@@ -218,8 +221,7 @@ public class UsageStatsService extends SystemService implements
* Called by the Binder stub.
*/
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) {
- final long timeNow = System.currentTimeMillis();
- if (beginTime > timeNow) {
+ if (!validRange(beginTime, endTime)) {
return null;
}
@@ -232,15 +234,23 @@ public class UsageStatsService extends SystemService implements
/**
* Called by the Binder stub.
*/
- UsageEvents queryEvents(int userId, long beginTime, long endTime) {
- final long timeNow = System.currentTimeMillis();
+ List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
+ long endTime) {
+ if (!validRange(beginTime, endTime)) {
+ return null;
+ }
- // Adjust the endTime so that we don't query for the latest events.
- // This is to prevent apps from making decision based on what app launched them,
- // etc.
- endTime = Math.min(endTime, timeNow - END_TIME_DELAY);
+ synchronized (mLock) {
+ UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId);
+ return service.queryConfigurationStats(bucketType, beginTime, endTime);
+ }
+ }
- if (beginTime > endTime) {
+ /**
+ * Called by the Binder stub.
+ */
+ UsageEvents queryEvents(int userId, long beginTime, long endTime) {
+ if (!validRange(beginTime, endTime)) {
return null;
}
@@ -250,6 +260,11 @@ public class UsageStatsService extends SystemService implements
}
}
+ private static boolean validRange(long beginTime, long endTime) {
+ final long timeNow = System.currentTimeMillis();
+ return beginTime <= timeNow && beginTime < endTime;
+ }
+
private void flushToDiskLocked() {
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
@@ -323,6 +338,28 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
+ long beginTime, long endTime, String callingPackage) throws RemoteException {
+ if (!hasPermission(callingPackage)) {
+ return null;
+ }
+
+ final int userId = UserHandle.getCallingUserId();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final List<ConfigurationStats> results =
+ UsageStatsService.this.queryConfigurationStats(userId, bucketType,
+ beginTime, endTime);
+ if (results != null) {
+ return new ParceledListSlice<>(results);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return null;
+ }
+
+ @Override
public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
if (!hasPermission(callingPackage)) {
return null;
@@ -346,8 +383,7 @@ public class UsageStatsService extends SystemService implements
private class LocalService extends UsageStatsManagerInternal {
@Override
- public void reportEvent(ComponentName component, int userId,
- long timeStamp, int eventType) {
+ public void reportEvent(ComponentName component, int userId, int eventType) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
@@ -356,12 +392,27 @@ public class UsageStatsService extends SystemService implements
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
- event.mTimeStamp = timeStamp;
+ event.mTimeStamp = System.currentTimeMillis();
event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
+ public void reportConfigurationChange(Configuration config, int userId) {
+ if (config == null) {
+ Slog.w(TAG, "Configuration event reported with a null config");
+ return;
+ }
+
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = "android";
+ event.mTimeStamp = System.currentTimeMillis();
+ event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
+ event.mConfiguration = new Configuration(config);
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ }
+
+ @Override
public void prepareShutdown() {
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 374429a..6529950 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,68 +20,69 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.ComponentName;
+import android.content.res.Configuration;
import java.io.IOException;
import java.net.ProtocolException;
+import java.util.Locale;
/**
* UsageStats reader/writer for version 1 of the XML format.
*/
final class UsageStatsXmlV1 {
+ private static final String PACKAGE_TAG = "package";
+ private static final String CONFIGURATION_TAG = "config";
+ private static final String EVENT_LOG_TAG = "event-log";
+
private static final String BEGIN_TIME_ATTR = "beginTime";
private static final String END_TIME_ATTR = "endTime";
- private static final String PACKAGE_TAG = "package";
private static final String NAME_ATTR = "name";
private static final String PACKAGE_ATTR = "package";
private static final String CLASS_ATTR = "class";
private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String COUNT_ATTR = "count";
+ private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
- private static final String EVENT_LOG_TAG = "event-log";
private static final String TYPE_ATTR = "type";
private static final String TIME_ATTR = "time";
- private static UsageStats readNextUsageStats(XmlPullParser parser)
+ private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- XmlUtils.nextElement(parser);
- }
-
- if (parser.getEventType() != XmlPullParser.START_TAG ||
- !parser.getName().equals(PACKAGE_TAG)) {
- return null;
- }
-
final String name = parser.getAttributeValue(null, NAME_ATTR);
if (name == null) {
throw new ProtocolException("no " + NAME_ATTR + " attribute present");
}
- UsageStats stats = new UsageStats();
- stats.mPackageName = name;
+ UsageStats stats = statsOut.getOrCreateUsageStats(name);
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
- XmlUtils.skipCurrentTag(parser);
- return stats;
}
- private static UsageEvents.Event readNextEvent(XmlPullParser parser, IntervalStats statsOut)
+ private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- XmlUtils.nextElement(parser);
- }
-
- if (parser.getEventType() != XmlPullParser.START_TAG ||
- !parser.getName().equals(EVENT_LOG_TAG)) {
- return null;
+ final Configuration config = new Configuration();
+ Configuration.readXmlAttrs(parser, config);
+
+ ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
+ configStats.mLastTimeActive = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
+ configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
+ configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
+ if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
+ statsOut.activeConfiguration = configStats.mConfiguration;
}
+ }
+ private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
+ throws XmlPullParserException, IOException {
String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
String className;
if (packageName == null) {
@@ -105,31 +106,60 @@ final class UsageStatsXmlV1 {
UsageEvents.Event event = statsOut.buildEvent(packageName, className);
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
- XmlUtils.skipCurrentTag(parser);
- return event;
+
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ event.mConfiguration = new Configuration();
+ Configuration.readXmlAttrs(parser, event.mConfiguration);
+ }
+
+ if (statsOut.events == null) {
+ statsOut.events = new TimeSparseArray<>();
+ }
+ statsOut.events.put(event.mTimeStamp, event);
}
- private static void writeUsageStats(FastXmlSerializer serializer, UsageStats stats)
+ private static void writeUsageStats(XmlSerializer xml, final UsageStats stats)
throws IOException {
- serializer.startTag(null, PACKAGE_TAG);
- serializer.attribute(null, NAME_ATTR, stats.mPackageName);
- serializer.attribute(null, TOTAL_TIME_ACTIVE_ATTR,
- Long.toString(stats.mTotalTimeInForeground));
- serializer.attribute(null, LAST_TIME_ACTIVE_ATTR, Long.toString(stats.mLastTimeUsed));
- serializer.attribute(null, LAST_EVENT_ATTR, Integer.toString(stats.mLastEvent));
- serializer.endTag(null, PACKAGE_TAG);
+ xml.startTag(null, PACKAGE_TAG);
+ XmlUtils.writeStringAttribute(xml, NAME_ATTR, stats.mPackageName);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeInForeground);
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeUsed);
+ XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, stats.mLastEvent);
+ xml.endTag(null, PACKAGE_TAG);
+ }
+
+ private static void writeConfigStats(XmlSerializer xml, final ConfigurationStats stats,
+ boolean isActive) throws IOException {
+ xml.startTag(null, CONFIGURATION_TAG);
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeActive);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeActive);
+ XmlUtils.writeIntAttribute(xml, COUNT_ATTR, stats.mActivationCount);
+ if (isActive) {
+ XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
+ }
+
+ // Now write the attributes representing the configuration object.
+ Configuration.writeXmlAttrs(xml, stats.mConfiguration);
+
+ xml.endTag(null, CONFIGURATION_TAG);
}
- private static void writeEvent(FastXmlSerializer serializer, UsageEvents.Event event)
+ private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event)
throws IOException {
- serializer.startTag(null, EVENT_LOG_TAG);
- serializer.attribute(null, PACKAGE_ATTR, event.mPackage);
+ xml.startTag(null, EVENT_LOG_TAG);
+ XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
if (event.mClass != null) {
- serializer.attribute(null, CLASS_ATTR, event.mClass);
+ XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
+ }
+ XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+ XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp);
+
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
+ && event.mConfiguration != null) {
+ Configuration.writeXmlAttrs(xml, event.mConfiguration);
}
- serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType()));
- serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp()));
- serializer.endTag(null, EVENT_LOG_TAG);
+
+ xml.endTag(null, EVENT_LOG_TAG);
}
/**
@@ -142,6 +172,8 @@ final class UsageStatsXmlV1 {
public static void read(XmlPullParser parser, IntervalStats statsOut)
throws XmlPullParserException, IOException {
statsOut.stats.clear();
+ statsOut.configurations.clear();
+ statsOut.activeConfiguration = null;
if (statsOut.events != null) {
statsOut.events.clear();
@@ -149,21 +181,29 @@ final class UsageStatsXmlV1 {
statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
- XmlUtils.nextElement(parser);
- UsageStats pkgStats;
- while ((pkgStats = readNextUsageStats(parser)) != null) {
- pkgStats.mBeginTimeStamp = statsOut.beginTime;
- pkgStats.mEndTimeStamp = statsOut.endTime;
- statsOut.stats.put(pkgStats.mPackageName, pkgStats);
- }
+ int eventCode;
+ int outerDepth = parser.getDepth();
+ while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (eventCode != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String tag = parser.getName();
+ switch (tag) {
+ case PACKAGE_TAG:
+ loadUsageStats(parser, statsOut);
+ break;
- UsageEvents.Event event;
- while ((event = readNextEvent(parser, statsOut)) != null) {
- if (statsOut.events == null) {
- statsOut.events = new TimeSparseArray<>();
+ case CONFIGURATION_TAG:
+ loadConfigStats(parser, statsOut);
+ break;
+
+ case EVENT_LOG_TAG:
+ loadEvent(parser, statsOut);
+ break;
}
- statsOut.events.put(event.getTimeStamp(), event);
}
}
@@ -184,11 +224,15 @@ final class UsageStatsXmlV1 {
writeUsageStats(serializer, stats.stats.valueAt(i));
}
- if (stats.events != null) {
- final int eventCount = stats.events.size();
- for (int i = 0; i < eventCount; i++) {
- writeEvent(serializer, stats.events.valueAt(i));
- }
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+ writeConfigStats(serializer, stats.configurations.valueAt(i), active);
+ }
+
+ final int eventCount = stats.events != null ? stats.events.size() : 0;
+ for (int i = 0; i < eventCount; i++) {
+ writeEvent(serializer, stats.events.valueAt(i));
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 6951590..7142a99 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,13 +16,17 @@
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
+import android.content.res.Configuration;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
@@ -108,35 +112,91 @@ class UserUsageStatsService {
notifyStatsChanged();
}
}
+
+ stat.updateConfigurationStats(null, stat.lastTimeSaved);
}
}
void reportEvent(UsageEvents.Event event) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
- + "[" + event.getTimeStamp() + "]: "
- + eventToString(event.getEventType()));
+ + "[" + event.mTimeStamp + "]: "
+ + eventToString(event.mEventType));
}
- if (event.getTimeStamp() >= mDailyExpiryDate.getTimeInMillis()) {
+ if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats();
}
- if (mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events == null) {
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events = new TimeSparseArray<>();
+ final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+
+ final Configuration newFullConfig = event.mConfiguration;
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
+ currentDailyStats.activeConfiguration != null) {
+ // Make the event configuration a delta.
+ event.mConfiguration = Configuration.generateDelta(
+ currentDailyStats.activeConfiguration, newFullConfig);
+ }
+
+ // Add the event to the daily list.
+ if (currentDailyStats.events == null) {
+ currentDailyStats.events = new TimeSparseArray<>();
}
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event);
+ currentDailyStats.events.put(event.mTimeStamp, event);
for (IntervalStats stats : mCurrentStats) {
- stats.update(event.mPackage, event.getTimeStamp(),
- event.getEventType());
+ if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+ stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+ } else {
+ stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ }
}
notifyStatsChanged();
}
- List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+ private static final StatCombiner<UsageStats> sUsageStatsCombiner =
+ new StatCombiner<UsageStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<UsageStats> accResult) {
+ if (!mutable) {
+ accResult.addAll(stats.stats.values());
+ return;
+ }
+
+ final int statCount = stats.stats.size();
+ for (int i = 0; i < statCount; i++) {
+ accResult.add(new UsageStats(stats.stats.valueAt(i)));
+ }
+ }
+ };
+
+ private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
+ new StatCombiner<ConfigurationStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<ConfigurationStats> accResult) {
+ if (!mutable) {
+ accResult.addAll(stats.configurations.values());
+ return;
+ }
+
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
+ }
+ }
+ };
+
+ /**
+ * Generic query method that selects the appropriate IntervalStats for the specified time range
+ * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
+ * provided to select the stats to use from the IntervalStats object.
+ */
+ private <T> List<T> queryStats(int bucketType, long beginTime, long endTime,
+ StatCombiner<T> combiner) {
if (bucketType == UsageStatsManager.INTERVAL_BEST) {
bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
}
@@ -161,11 +221,8 @@ class UserUsageStatsService {
Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
}
// Fast path for retrieving in-memory state.
- ArrayList<UsageStats> results = new ArrayList<>();
- final int packageCount = mCurrentStats[bucketType].stats.size();
- for (int i = 0; i < packageCount; i++) {
- results.add(new UsageStats(mCurrentStats[bucketType].stats.valueAt(i)));
- }
+ ArrayList<T> results = new ArrayList<>();
+ combiner.combine(mCurrentStats[bucketType], true, results);
return results;
}
@@ -178,13 +235,21 @@ class UserUsageStatsService {
+ beginTime + " AND endTime < " + endTime);
}
- final List<UsageStats> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime);
+ final List<T> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime, combiner);
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
}
return results;
}
+ List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+ return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
+ }
+
+ List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
+ return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
+ }
+
UsageEvents queryEvents(long beginTime, long endTime) {
if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
@@ -245,6 +310,8 @@ class UserUsageStatsService {
// Finish any ongoing events with an END_OF_DAY event. Make a note of which components
// need a new CONTINUE_PREVIOUS_DAY entry.
+ final Configuration previousConfig =
+ mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
ArraySet<String> continuePreviousDay = new ArraySet<>();
for (IntervalStats stat : mCurrentStats) {
final int pkgCount = stat.stats.size();
@@ -253,11 +320,13 @@ class UserUsageStatsService {
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
continuePreviousDay.add(pkgStats.mPackageName);
- stat.update(pkgStats.mPackageName,
- mDailyExpiryDate.getTimeInMillis() - 1, UsageEvents.Event.END_OF_DAY);
+ stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
+ UsageEvents.Event.END_OF_DAY);
mStatsChanged = true;
}
}
+
+ stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
}
persistActiveStats();
@@ -267,9 +336,10 @@ class UserUsageStatsService {
final int continueCount = continuePreviousDay.size();
for (int i = 0; i < continueCount; i++) {
String name = continuePreviousDay.valueAt(i);
+ final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
for (IntervalStats stat : mCurrentStats) {
- stat.update(name, mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime,
- UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ stat.updateConfigurationStats(previousConfig, beginTime);
mStatsChanged = true;
}
}
@@ -353,6 +423,8 @@ class UserUsageStatsService {
return "END_OF_DAY";
case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
return "CONTINUE_PREVIOUS_DAY";
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ return "CONFIGURATION_CHANGE";
default:
return "UNKNOWN";
}