Split up ComponentName in UsageEvents.Event

Some events in the future may not have originated
from a class, so we shouldn't be using ComponentName.

Bug:17259858
Change-Id: Id7fe3245b91596cf27ae4ec51655602f01665622
This commit is contained in:
Adam Lesinski
2014-08-25 14:48:12 -07:00
parent 4e9c07c0de
commit 9d9607527f
7 changed files with 125 additions and 54 deletions

View File

@@ -5715,8 +5715,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

View File

@@ -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;
}
p.writeInt(index);
final int classIndex;
if (event.mClass != null) {
classIndex = findStringIndex(event.mClass);
} else {
classIndex = -1;
}
p.writeInt(packageIndex);
p.writeInt(classIndex);
p.writeInt(event.getEventType());
p.writeLong(event.getTimeStamp());
}

View File

@@ -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 name;
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;
}
}

View File

@@ -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();

View File

@@ -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");
}
packageName = component.getPackageName();
className = component.getClassName();
} else {
className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
}
ComponentName component = statsOut.getCachedComponentName(componentName);
if (component == null) {
throw new ProtocolException("ComponentName " + componentName + " is invalid");
}
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);

View File

@@ -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);
}

View File

@@ -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: