diff options
7 files changed, 124 insertions, 53 deletions
diff --git a/api/current.txt b/api/current.txt index ba97e21..a820163 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5719,8 +5719,9 @@ package android.app.usage { public static final class UsageEvents.Event { ctor public UsageEvents.Event(); - method public android.content.ComponentName getComponent(); + method public java.lang.String getClassName(); method public int getEventType(); + method public java.lang.String getPackageName(); method public long getTimeStamp(); field public static final int MOVE_TO_BACKGROUND = 2; // 0x2 field public static final int MOVE_TO_FOREGROUND = 1; // 0x1 diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index d1ebc5f..2431ad0 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -65,7 +65,12 @@ public final class UsageEvents implements Parcelable { /** * {@hide} */ - public ComponentName mComponent; + public String mPackage; + + /** + * {@hide} + */ + public String mClass; /** * {@hide} @@ -78,10 +83,26 @@ public final class UsageEvents implements Parcelable { public int mEventType; /** - * The component this event represents. + * TODO(adamlesinski): Removed before release. + * {@hide} */ public ComponentName getComponent() { - return mComponent; + return new ComponentName(mPackage, mClass); + } + + /** + * The package name of the source of this event. + */ + public String getPackageName() { + return mPackage; + } + + /** + * The class name of the source of this event. This may be null for + * certain events. + */ + public String getClassName() { + return mClass; } /** @@ -115,7 +136,7 @@ public final class UsageEvents implements Parcelable { * In order to save space, since ComponentNames will be duplicated everywhere, * we use a map and index into it. */ - private ComponentName[] mComponentNameTable; + private String[] mStringPool; /** * Construct the iterator from a parcel. @@ -125,7 +146,7 @@ public final class UsageEvents implements Parcelable { mEventCount = in.readInt(); mIndex = in.readInt(); if (mEventCount > 0) { - mComponentNameTable = in.createTypedArray(ComponentName.CREATOR); + mStringPool = in.createStringArray(); final int listByteLength = in.readInt(); final int positionInParcel = in.readInt(); @@ -149,8 +170,8 @@ public final class UsageEvents implements Parcelable { * Construct the iterator in preparation for writing it to a parcel. * {@hide} */ - public UsageEvents(List<Event> events, ComponentName[] nameTable) { - mComponentNameTable = nameTable; + public UsageEvents(List<Event> events, String[] stringPool) { + mStringPool = stringPool; mEventCount = events.size(); mEventsToWrite = events; } @@ -178,8 +199,19 @@ public final class UsageEvents implements Parcelable { return false; } - final int index = mParcel.readInt(); - eventOut.mComponent = mComponentNameTable[index]; + final int packageIndex = mParcel.readInt(); + if (packageIndex >= 0) { + eventOut.mPackage = mStringPool[packageIndex]; + } else { + eventOut.mPackage = null; + } + + final int classIndex = mParcel.readInt(); + if (classIndex >= 0) { + eventOut.mClass = mStringPool[classIndex]; + } else { + eventOut.mClass = null; + } eventOut.mEventType = mParcel.readInt(); eventOut.mTimeStamp = mParcel.readLong(); mIndex++; @@ -206,12 +238,20 @@ public final class UsageEvents implements Parcelable { return 0; } + private int findStringIndex(String str) { + final int index = Arrays.binarySearch(mStringPool, str); + if (index < 0) { + throw new IllegalStateException("String '" + str + "' is not in the string pool"); + } + return index; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mEventCount); dest.writeInt(mIndex); if (mEventCount > 0) { - dest.writeTypedArray(mComponentNameTable, flags); + dest.writeStringArray(mStringPool); if (mEventsToWrite != null) { // Write out the events @@ -221,12 +261,21 @@ public final class UsageEvents implements Parcelable { for (int i = 0; i < mEventCount; i++) { final Event event = mEventsToWrite.get(i); - int index = Arrays.binarySearch(mComponentNameTable, event.getComponent()); - if (index < 0) { - throw new IllegalStateException(event.getComponent().toShortString() + - " is not in the component name table"); + final int packageIndex; + if (event.mPackage != null) { + packageIndex = findStringIndex(event.mPackage); + } else { + packageIndex = -1; + } + + final int classIndex; + if (event.mClass != null) { + classIndex = findStringIndex(event.mClass); + } else { + classIndex = -1; } - p.writeInt(index); + p.writeInt(packageIndex); + p.writeInt(classIndex); p.writeInt(event.getEventType()); p.writeLong(event.getTimeStamp()); } diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 43027ad..dc036e2 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -18,8 +18,8 @@ package com.android.server.usage; import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; -import android.content.ComponentName; import android.util.ArrayMap; +import android.util.ArraySet; class IntervalStats { public long beginTime; @@ -28,10 +28,11 @@ class IntervalStats { public final ArrayMap<String, UsageStats> stats = new ArrayMap<>(); public TimeSparseArray<UsageEvents.Event> events; - // Maps flattened string representations of component names to ComponentName. - // This helps save memory from using many duplicate ComponentNames and - // parse time when reading XML. - private final ArrayMap<String, ComponentName> mComponentNames = new ArrayMap<>(); + // A string cache. This is important as when we're parsing XML files, we don't want to + // keep hundreds of strings that have the same contents. We will read the string + // and only keep it if it's not in the cache. The GC will take care of the + // strings that had identical copies in the cache. + private final ArraySet<String> mStringCache = new ArraySet<>(); UsageStats getOrCreateUsageStats(String packageName) { UsageStats usageStats = stats.get(packageName); @@ -63,19 +64,21 @@ class IntervalStats { endTime = timeStamp; } - /** - * Return a ComponentName for the given string representation. This will use a cached - * copy of the ComponentName if possible, otherwise it will parse and add it to the - * internal cache. - */ - ComponentName getCachedComponentName(String str) { - ComponentName name = mComponentNames.get(str); - if (name == null) { - name = ComponentName.unflattenFromString(str); - if (name != null) { - mComponentNames.put(str, name); - } + private String getCachedStringRef(String str) { + final int index = mStringCache.indexOf(str); + if (index < 0) { + mStringCache.add(str); + return str; + } + 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 name; + return event; } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 82e837d..0e8b427 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -354,7 +354,8 @@ public class UsageStatsService extends SystemService implements } UsageEvents.Event event = new UsageEvents.Event(); - event.mComponent = component; + event.mPackage = component.getPackageName(); + event.mClass = component.getClassName(); event.mTimeStamp = timeStamp; event.mEventType = eventType; mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index 916601b..374429a 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -37,6 +37,8 @@ final class UsageStatsXmlV1 { 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 LAST_EVENT_ATTR = "lastEvent"; @@ -80,18 +82,27 @@ final class UsageStatsXmlV1 { return null; } - final String componentName = XmlUtils.readStringAttribute(parser, NAME_ATTR); - if (componentName == null) { - throw new ProtocolException("no " + NAME_ATTR + " attribute present"); - } + String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR); + String className; + if (packageName == null) { + // Try getting the component name if it exists. + final String componentName = XmlUtils.readStringAttribute(parser, NAME_ATTR); + if (componentName == null) { + throw new ProtocolException("no " + NAME_ATTR + " or " + PACKAGE_ATTR + + " attribute present"); + } + ComponentName component = ComponentName.unflattenFromString(componentName); + if (component == null) { + throw new ProtocolException("ComponentName " + componentName + " is invalid"); + } - ComponentName component = statsOut.getCachedComponentName(componentName); - if (component == null) { - throw new ProtocolException("ComponentName " + componentName + " is invalid"); + packageName = component.getPackageName(); + className = component.getClassName(); + } else { + className = XmlUtils.readStringAttribute(parser, CLASS_ATTR); } - UsageEvents.Event event = new UsageEvents.Event(); - event.mComponent = component; + UsageEvents.Event event = statsOut.buildEvent(packageName, className); event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR); event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR); XmlUtils.skipCurrentTag(parser); @@ -112,7 +123,10 @@ final class UsageStatsXmlV1 { private static void writeEvent(FastXmlSerializer serializer, UsageEvents.Event event) throws IOException { serializer.startTag(null, EVENT_LOG_TAG); - serializer.attribute(null, NAME_ATTR, event.getComponent().flattenToString()); + serializer.attribute(null, PACKAGE_ATTR, event.mPackage); + if (event.mClass != null) { + serializer.attribute(null, CLASS_ATTR, event.mClass); + } serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType())); serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp())); serializer.endTag(null, EVENT_LOG_TAG); diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index e489fb3..6951590 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -20,7 +20,6 @@ import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; -import android.content.ComponentName; import android.util.ArraySet; import android.util.Slog; @@ -114,7 +113,7 @@ class UserUsageStatsService { void reportEvent(UsageEvents.Event event) { if (DEBUG) { - Slog.d(TAG, mLogPrefix + "Got usage event for " + event.getComponent().getPackageName() + Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage + "[" + event.getTimeStamp() + "]: " + eventToString(event.getEventType())); } @@ -130,7 +129,7 @@ class UserUsageStatsService { mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event); for (IntervalStats stats : mCurrentStats) { - stats.update(event.getComponent().getPackageName(), event.getTimeStamp(), + stats.update(event.mPackage, event.getTimeStamp(), event.getEventType()); } @@ -203,17 +202,21 @@ class UserUsageStatsService { return null; } - ArraySet<ComponentName> names = new ArraySet<>(); + ArraySet<String> names = new ArraySet<>(); ArrayList<UsageEvents.Event> results = new ArrayList<>(); final int size = events.size(); for (int i = startIndex; i < size; i++) { if (events.keyAt(i) >= endTime) { break; } - names.add(events.valueAt(i).getComponent()); - results.add(events.valueAt(i)); + final UsageEvents.Event event = events.valueAt(i); + names.add(event.mPackage); + if (event.mClass != null) { + names.add(event.mClass); + } + results.add(event); } - ComponentName[] table = names.toArray(new ComponentName[names.size()]); + String[] table = names.toArray(new String[names.size()]); Arrays.sort(table); return new UsageEvents(results, table); } diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java index 5d8fc9e..5f62ad8 100644 --- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java +++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java @@ -108,7 +108,7 @@ public class UsageLogActivity extends ListActivity implements Runnable { holder = (ViewHolder) convertView.getTag(); } - holder.packageName.setText(mEvents.get(position).getComponent().toShortString()); + holder.packageName.setText(mEvents.get(position).getPackageName()); String state; switch (mEvents.get(position).getEventType()) { case UsageEvents.Event.MOVE_TO_FOREGROUND: |