diff options
24 files changed, 1098 insertions, 538 deletions
diff --git a/api/current.txt b/api/current.txt index 33ae6433bd05..0a2d1673907b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7614,13 +7614,16 @@ package android.app.usage { method public java.lang.String getPackageName(); method public java.lang.String getShortcutId(); method public long getTimeStamp(); + field public static final int ACTIVITY_PAUSED = 2; // 0x2 + field public static final int ACTIVITY_RESUMED = 1; // 0x1 + field public static final int ACTIVITY_STOPPED = 23; // 0x17 field public static final int CONFIGURATION_CHANGE = 5; // 0x5 field public static final int FOREGROUND_SERVICE_START = 19; // 0x13 field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14 field public static final int KEYGUARD_HIDDEN = 18; // 0x12 field public static final int KEYGUARD_SHOWN = 17; // 0x11 - field public static final int MOVE_TO_BACKGROUND = 2; // 0x2 - field public static final int MOVE_TO_FOREGROUND = 1; // 0x1 + field public static final deprecated int MOVE_TO_BACKGROUND = 2; // 0x2 + field public static final deprecated int MOVE_TO_FOREGROUND = 1; // 0x1 field public static final int NONE = 0; // 0x0 field public static final int SCREEN_INTERACTIVE = 15; // 0xf field public static final int SCREEN_NON_INTERACTIVE = 16; // 0x10 @@ -7637,9 +7640,11 @@ package android.app.usage { method public long getLastTimeForegroundServiceUsed(); method public long getLastTimeStamp(); method public long getLastTimeUsed(); + method public long getLastTimeVisible(); method public java.lang.String getPackageName(); method public long getTotalTimeForegroundServiceUsed(); method public long getTotalTimeInForeground(); + method public long getTotalTimeVisible(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.usage.UsageStats> CREATOR; } diff --git a/api/system-current.txt b/api/system-current.txt index 29089b3e908a..96ac2e616f46 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -935,6 +935,7 @@ package android.app.usage { } public static final class UsageEvents.Event { + method public int getInstanceId(); method public java.lang.String getNotificationChannelId(); field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc field public static final int NOTIFICATION_SEEN = 10; // 0xa diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index af3da0cbf5ee..f928501b7c1a 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -196,8 +196,26 @@ public abstract class ActivityManagerInternal { public abstract void updateOomAdj(); public abstract void updateCpuStats(); - public abstract void updateUsageStats( + + /** + * Update battery stats on activity usage. + * @param activity + * @param uid + * @param userId + * @param started + */ + public abstract void updateBatteryStats( ComponentName activity, int uid, int userId, boolean resumed); + + /** + * Update UsageStats of the activity. + * @param activity + * @param userId + * @param event + * @param appToken ActivityRecord's appToken. + */ + public abstract void updateActivityUsageStats( + ComponentName activity, int userId, int event, IBinder appToken); public abstract void updateForegroundTimeIfOnBattery( String packageName, int uid, long cpuTimeDiff); public abstract void sendForegroundProfileChanged(int userId); diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java index aaae57e526a0..a79ad2fc8607 100644 --- a/core/java/android/app/usage/EventList.java +++ b/core/java/android/app/usage/EventList.java @@ -103,4 +103,21 @@ public class EventList { } return result; } + + /** + * Remove events of certain type on or after a timestamp. + * @param type The type of event to remove. + * @param timeStamp the timeStamp on or after which to remove the event. + */ + public void removeOnOrAfter(int type, long timeStamp) { + for (int i = mEvents.size() - 1; i >= 0; i--) { + UsageEvents.Event event = mEvents.get(i); + if (event.mTimeStamp < timeStamp) { + break; + } + if (event.mEventType == type) { + mEvents.remove(i); + } + } + } } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 3a5975aea628..a06213d77a68 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -50,13 +50,27 @@ public final class UsageEvents implements Parcelable { public static final int NONE = 0; /** + * @deprecated by {@link #ACTIVITY_RESUMED} + */ + @Deprecated + public static final int MOVE_TO_FOREGROUND = 1; + + /** * An event type denoting that an {@link android.app.Activity} moved to the foreground. * This event has a package name and class name associated with it and can be retrieved * using {@link #getPackageName()} and {@link #getClassName()}. * If a package has multiple activities, this event is reported for each activity that moves * to foreground. + * This event is corresponding to {@link android.app.Activity#onResume()} of the + * activity's lifecycle. */ - public static final int MOVE_TO_FOREGROUND = 1; + public static final int ACTIVITY_RESUMED = MOVE_TO_FOREGROUND; + + /** + * @deprecated by {@link #ACTIVITY_PAUSED} + */ + @Deprecated + public static final int MOVE_TO_BACKGROUND = 2; /** * An event type denoting that an {@link android.app.Activity} moved to the background. @@ -64,19 +78,21 @@ public final class UsageEvents implements Parcelable { * using {@link #getPackageName()} and {@link #getClassName()}. * If a package has multiple activities, this event is reported for each activity that moves * to background. + * This event is corresponding to {@link android.app.Activity#onPause()} of the activity's + * lifecycle. */ - public static final int MOVE_TO_BACKGROUND = 2; + public static final int ACTIVITY_PAUSED = MOVE_TO_BACKGROUND; /** * An event type denoting that a component was in the foreground when the stats - * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}. + * rolled-over. This is effectively treated as a {@link #ACTIVITY_PAUSED}. * {@hide} */ public static final int END_OF_DAY = 3; /** * An event type denoting that a component was in the foreground the previous day. - * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}. + * This is effectively treated as a {@link #ACTIVITY_RESUMED}. * {@hide} */ public static final int CONTINUE_PREVIOUS_DAY = 4; @@ -207,10 +223,31 @@ public final class UsageEvents implements Parcelable { public static final int ROLLOVER_FOREGROUND_SERVICE = 22; /** + * An activity becomes invisible on the UI, corresponding to + * {@link android.app.Activity#onStop()} of the activity's lifecycle. + */ + public static final int ACTIVITY_STOPPED = 23; + + /** + * An activity object is destroyed, corresponding to + * {@link android.app.Activity#onDestroy()} of the activity's lifecycle. + * {@hide} + */ + public static final int ACTIVITY_DESTROYED = 24; + + /** + * The event type demoting that a flush of UsageStatsDatabase to file system. Before the + * flush all usage stats need to be updated to latest timestamp to make sure the most + * up to date stats are persisted. + * @hide + */ + public static final int FLUSH_TO_DISK = 25; + + /** * Keep in sync with the greatest event type value. * @hide */ - public static final int MAX_EVENT_TYPE = 22; + public static final int MAX_EVENT_TYPE = 25; /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; @@ -240,6 +277,12 @@ public final class UsageEvents implements Parcelable { @UnsupportedAppUsage public String mClass; + + /** + * {@hide} + */ + public int mInstanceId; + /** * {@hide} */ @@ -311,9 +354,16 @@ public final class UsageEvents implements Parcelable { } /** @hide */ + public Event(int type, long timeStamp) { + mEventType = type; + mTimeStamp = timeStamp; + } + + /** @hide */ public Event(Event orig) { mPackage = orig.mPackage; mClass = orig.mClass; + mInstanceId = orig.mInstanceId; mTimeStamp = orig.mTimeStamp; mEventType = orig.mEventType; mConfiguration = orig.mConfiguration; @@ -342,6 +392,16 @@ public final class UsageEvents implements Parcelable { } /** + * An activity can be instantiated multiple times, this is the unique activity instance ID. + * For non-activity class, instance ID is always zero. + * @hide + */ + @SystemApi + public int getInstanceId() { + return mInstanceId; + } + + /** * The time at which this event occurred, measured in milliseconds since the epoch. * <p/> * See {@link System#currentTimeMillis()}. @@ -352,12 +412,14 @@ public final class UsageEvents implements Parcelable { /** * The event type. - * - * @see #MOVE_TO_BACKGROUND - * @see #MOVE_TO_FOREGROUND + * @see #ACTIVITY_PAUSED + * @see #ACTIVITY_RESUMED * @see #CONFIGURATION_CHANGE * @see #USER_INTERACTION * @see #STANDBY_BUCKET_CHANGED + * @see #FOREGROUND_SERVICE_START + * @see #FOREGROUND_SERVICE_STOP + * @see #ACTIVITY_STOPPED */ public int getEventType() { return mEventType; @@ -576,6 +638,7 @@ public final class UsageEvents implements Parcelable { } p.writeInt(packageIndex); p.writeInt(classIndex); + p.writeInt(event.mInstanceId); p.writeInt(event.mEventType); p.writeLong(event.mTimeStamp); @@ -618,6 +681,7 @@ public final class UsageEvents implements Parcelable { } else { eventOut.mClass = null; } + eventOut.mInstanceId = p.readInt(); eventOut.mEventType = p.readInt(); eventOut.mTimeStamp = p.readLong(); diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 73426e495037..8fb7f4cb4d99 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -16,13 +16,15 @@ package android.app.usage; -import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY; +import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; import static android.app.usage.UsageEvents.Event.END_OF_DAY; +import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP; -import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND; -import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND; import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE; import android.annotation.SystemApi; @@ -31,6 +33,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; +import android.util.SparseIntArray; /** * Contains usage statistics for an app package for a specific @@ -57,13 +60,20 @@ public final class UsageStats implements Parcelable { public long mEndTimeStamp; /** - * Last time used by the user with an explicit action (notification, activity launch) + * Last time an activity is at foreground (have focus), this is corresponding to + * {@link android.app.usage.UsageEvents.Event#ACTIVITY_RESUMED} event. * {@hide} */ @UnsupportedAppUsage public long mLastTimeUsed; /** + * Last time an activity is visible. + * @hide + */ + public long mLastTimeVisible; + + /** * Total time this package's activity is in foreground. * {@hide} */ @@ -71,6 +81,12 @@ public final class UsageStats implements Parcelable { public long mTotalTimeInForeground; /** + * Total time this package's activity is visible. + * {@hide} + */ + public long mTotalTimeVisible; + + /** * Last time foreground service is started. * {@hide} */ @@ -93,31 +109,32 @@ public final class UsageStats implements Parcelable { */ public int mAppLaunchCount; - /** Last activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event. + /** Last activity ACTIVITY_RESUMED or ACTIVITY_PAUSED event. * {@hide} - * @deprecated use {@link #mLastForegroundActivityEventMap} instead. + * @deprecated use {@link #mActivities} instead. */ @UnsupportedAppUsage @Deprecated public int mLastEvent; /** - * If an activity is in foreground, it has one entry in this map. - * When activity moves to background, it is removed from this map. - * Key is activity class name. - * Value is last time this activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event. + * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state), + * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()), + * it is removed from this map. + * Key is instanceId of the activity (ActivityRecode appToken hashCode).. + * Value is this activity's last event, one of ACTIVITY_RESUMED or + * ACTIVITY_PAUSED. * {@hide} */ - public ArrayMap<String, Integer> mLastForegroundActivityEventMap = new ArrayMap<>(); - + public SparseIntArray mActivities = new SparseIntArray(); /** * If a foreground service is started, it has one entry in this map. - * When a foreground service is stopped, it is removed from this map. + * When a foreground service is stopped, it is removed from this set. * Key is foreground service class name. - * Value is last foreground service FOREGROUND_SERVICE_START ot FOREGROUND_SERVICE_STOP event. + * Value is the foreground service's last event, it is FOREGROUND_SERVICE_START. * {@hide} */ - public ArrayMap<String, Integer> mLastForegroundServiceEventMap = new ArrayMap<>(); + public ArrayMap<String, Integer> mForegroundServices = new ArrayMap<>(); /** * {@hide} @@ -135,14 +152,16 @@ public final class UsageStats implements Parcelable { mBeginTimeStamp = stats.mBeginTimeStamp; mEndTimeStamp = stats.mEndTimeStamp; mLastTimeUsed = stats.mLastTimeUsed; + mLastTimeVisible = stats.mLastTimeVisible; mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed; mTotalTimeInForeground = stats.mTotalTimeInForeground; + mTotalTimeVisible = stats.mTotalTimeVisible; mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed; mLaunchCount = stats.mLaunchCount; mAppLaunchCount = stats.mAppLaunchCount; mLastEvent = stats.mLastEvent; - mLastForegroundActivityEventMap = stats.mLastForegroundActivityEventMap; - mLastForegroundServiceEventMap = stats.mLastForegroundServiceEventMap; + mActivities = stats.mActivities; + mForegroundServices = stats.mForegroundServices; mChooserCounts = stats.mChooserCounts; } @@ -191,6 +210,14 @@ public final class UsageStats implements Parcelable { } /** + * Get the last time this package's activity is visible in the UI, measured in milliseconds + * since the epoch. + */ + public long getLastTimeVisible() { + return mLastTimeVisible; + } + + /** * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { @@ -198,6 +225,13 @@ public final class UsageStats implements Parcelable { } /** + * Get the total time this package's activity is visible in the UI, measured in milliseconds. + */ + public long getTotalTimeVisible() { + return mTotalTimeVisible; + } + + /** * Get the last time this package's foreground service was used, measured in milliseconds since * the epoch. * <p/> @@ -224,6 +258,20 @@ public final class UsageStats implements Parcelable { return mAppLaunchCount; } + private void mergeEventMap(SparseIntArray left, SparseIntArray right) { + final int size = right.size(); + for (int i = 0; i < size; i++) { + final int instanceId = right.keyAt(i); + final int event = right.valueAt(i); + final int index = left.indexOfKey(instanceId); + if (index >= 0) { + left.put(instanceId, Math.max(left.valueAt(index), event)); + } else { + left.put(instanceId, event); + } + } + } + private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) { final int size = right.size(); for (int i = 0; i < size; i++) { @@ -255,15 +303,17 @@ public final class UsageStats implements Parcelable { if (right.mBeginTimeStamp > mBeginTimeStamp) { // Even though incoming UsageStat begins after this one, its last time used fields // may somehow be empty or chronologically preceding the older UsageStat. - mergeEventMap(mLastForegroundActivityEventMap, right.mLastForegroundActivityEventMap); - mergeEventMap(mLastForegroundServiceEventMap, right.mLastForegroundServiceEventMap); + mergeEventMap(mActivities, right.mActivities); + mergeEventMap(mForegroundServices, right.mForegroundServices); mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed); + mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible); mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed, right.mLastTimeForegroundServiceUsed); } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; + mTotalTimeVisible += right.mTotalTimeVisible; mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed; mLaunchCount += right.mLaunchCount; mAppLaunchCount += right.mAppLaunchCount; @@ -290,36 +340,76 @@ public final class UsageStats implements Parcelable { } /** - * Tell if an event indicate activity is in foreground or not. - * @param event the activity event. - * @return true if activity is in foreground, false otherwise. - * @hide + * Tell if any activity is in foreground. + * @return */ - private boolean isActivityInForeground(int event) { - return event == MOVE_TO_FOREGROUND - || event == CONTINUE_PREVIOUS_DAY; + private boolean hasForegroundActivity() { + final int size = mActivities.size(); + for (int i = 0; i < size; i++) { + if (mActivities.valueAt(i) == ACTIVITY_RESUMED) { + return true; + } + } + return false; } /** - * Tell if an event indicate foreground sevice is started or not. - * @param event the foreground service event. - * @return true if foreground service is started, false if stopped. - * @hide + * Tell if any activity is visible. + * @return */ - private boolean isForegroundServiceStarted(int event) { - return event == FOREGROUND_SERVICE_START - || event == CONTINUING_FOREGROUND_SERVICE; + private boolean hasVisibleActivity() { + final int size = mActivities.size(); + for (int i = 0; i < size; i++) { + final int type = mActivities.valueAt(i); + if (type == ACTIVITY_RESUMED + || type == ACTIVITY_PAUSED) { + return true; + } + } + return false; } /** - * If any activity in foreground or any foreground service is started, the app is considered in - * use. - * @return true if in use, false otherwise. - * @hide + * Tell if any foreground service is started. + * @return + */ + private boolean anyForegroundServiceStarted() { + return !mForegroundServices.isEmpty(); + } + + /** + * Increment total time in foreground and update last time in foreground. + * @param timeStamp current timestamp. + */ + private void incrementTimeUsed(long timeStamp) { + if (timeStamp > mLastTimeUsed) { + mTotalTimeInForeground += timeStamp - mLastTimeUsed; + mLastTimeUsed = timeStamp; + } + } + + /** + * Increment total time visible and update last time visible. + * @param timeStamp current timestmap. */ - private boolean isAppInUse() { - return !mLastForegroundActivityEventMap.isEmpty() - || !mLastForegroundServiceEventMap.isEmpty(); + private void incrementTimeVisible(long timeStamp) { + if (timeStamp > mLastTimeVisible) { + mTotalTimeVisible += timeStamp - mLastTimeVisible; + mLastTimeVisible = timeStamp; + } + } + + /** + * Increment total time foreground service is used and update last time foreground service is + * used. + * @param timeStamp current timestamp. + */ + private void incrementServiceTimeUsed(long timeStamp) { + if (timeStamp > mLastTimeForegroundServiceUsed) { + mTotalTimeForegroundServiceUsed += + timeStamp - mLastTimeForegroundServiceUsed; + mLastTimeForegroundServiceUsed = timeStamp; + } } /** @@ -327,33 +417,63 @@ public final class UsageStats implements Parcelable { * @param className className of the activity. * @param timeStamp timeStamp of the event. * @param eventType type of the event. + * @param instanceId hashCode of the ActivityRecord's appToken. * @hide */ - private void updateForegroundActivity(String className, long timeStamp, int eventType) { - if (eventType != MOVE_TO_BACKGROUND - && eventType != MOVE_TO_FOREGROUND - && eventType != END_OF_DAY) { + private void updateActivity(String className, long timeStamp, int eventType, int instanceId) { + if (eventType != ACTIVITY_RESUMED + && eventType != ACTIVITY_PAUSED + && eventType != ACTIVITY_STOPPED + && eventType != ACTIVITY_DESTROYED) { return; } - final Integer lastEvent = mLastForegroundActivityEventMap.get(className); - if (lastEvent != null) { - if (isActivityInForeground(lastEvent)) { - if (timeStamp > mLastTimeUsed) { - mTotalTimeInForeground += timeStamp - mLastTimeUsed; + // update usage. + final int index = mActivities.indexOfKey(instanceId); + if (index >= 0) { + final int lastEvent = mActivities.valueAt(index); + switch (lastEvent) { + case ACTIVITY_RESUMED: + incrementTimeUsed(timeStamp); + incrementTimeVisible(timeStamp); + break; + case ACTIVITY_PAUSED: + incrementTimeVisible(timeStamp); + break; + default: + break; + } + } + + // update current event. + switch(eventType) { + case ACTIVITY_RESUMED: + if (!hasVisibleActivity()) { + // this is the first visible activity. + mLastTimeUsed = timeStamp; + mLastTimeVisible = timeStamp; + } else if (!hasForegroundActivity()) { + // this is the first foreground activity. mLastTimeUsed = timeStamp; } - } - if (eventType == MOVE_TO_BACKGROUND) { - mLastForegroundActivityEventMap.remove(className); - } else { - mLastForegroundActivityEventMap.put(className, eventType); - } - } else if (eventType == MOVE_TO_FOREGROUND) { - if (!isAppInUse()) { - mLastTimeUsed = timeStamp; - } - mLastForegroundActivityEventMap.put(className, eventType); + mActivities.put(instanceId, eventType); + break; + case ACTIVITY_PAUSED: + if (!hasVisibleActivity()) { + // this is the first visible activity. + mLastTimeVisible = timeStamp; + } + mActivities.put(instanceId, eventType); + break; + case ACTIVITY_STOPPED: + mActivities.put(instanceId, eventType); + break; + case ACTIVITY_DESTROYED: + // remove activity from the map. + mActivities.delete(instanceId); + break; + default: + break; } } @@ -366,80 +486,97 @@ public final class UsageStats implements Parcelable { */ private void updateForegroundService(String className, long timeStamp, int eventType) { if (eventType != FOREGROUND_SERVICE_STOP - && eventType != FOREGROUND_SERVICE_START - && eventType != ROLLOVER_FOREGROUND_SERVICE) { + && eventType != FOREGROUND_SERVICE_START) { return; } - final Integer lastEvent = mLastForegroundServiceEventMap.get(className); + final Integer lastEvent = mForegroundServices.get(className); + // update usage. if (lastEvent != null) { - if (isForegroundServiceStarted(lastEvent)) { - if (timeStamp > mLastTimeForegroundServiceUsed) { - mTotalTimeForegroundServiceUsed += - timeStamp - mLastTimeForegroundServiceUsed; + switch (lastEvent) { + case FOREGROUND_SERVICE_START: + case CONTINUING_FOREGROUND_SERVICE: + incrementServiceTimeUsed(timeStamp); + break; + default: + break; + } + } + + // update current event. + switch (eventType) { + case FOREGROUND_SERVICE_START: + if (!anyForegroundServiceStarted()) { mLastTimeForegroundServiceUsed = timeStamp; } - } - if (eventType == FOREGROUND_SERVICE_STOP) { - mLastForegroundServiceEventMap.remove(className); - } else { - mLastForegroundServiceEventMap.put(className, eventType); - } - } else if (eventType == FOREGROUND_SERVICE_START) { - if (!isAppInUse()) { - mLastTimeForegroundServiceUsed = timeStamp; - } - mLastForegroundServiceEventMap.put(className, eventType); + mForegroundServices.put(className, eventType); + break; + case FOREGROUND_SERVICE_STOP: + mForegroundServices.remove(className); + break; + default: + break; } } /** * Update the UsageStats by a activity or foreground service event. - * @param className class name of a activity or foreground service, could be null to mark - * END_OF_DAY or rollover. + * @param className class name of a activity or foreground service, could be null to if this + * is sent to all activities/services in this package. * @param timeStamp Epoch timestamp in milliseconds. * @param eventType event type as in {@link UsageEvents.Event} + * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken. + * if className is not an activity, instanceId is not used. * @hide */ - public void update(String className, long timeStamp, int eventType) { + public void update(String className, long timeStamp, int eventType, int instanceId) { switch(eventType) { - case MOVE_TO_BACKGROUND: - case MOVE_TO_FOREGROUND: - updateForegroundActivity(className, timeStamp, eventType); + case ACTIVITY_RESUMED: + case ACTIVITY_PAUSED: + case ACTIVITY_STOPPED: + case ACTIVITY_DESTROYED: + updateActivity(className, timeStamp, eventType, instanceId); break; case END_OF_DAY: - // END_OF_DAY means updating all activities. - final int size = mLastForegroundActivityEventMap.size(); - for (int i = 0; i < size; i++) { - final String name = mLastForegroundActivityEventMap.keyAt(i); - updateForegroundActivity(name, timeStamp, eventType); + // END_OF_DAY updates all activities. + if (hasForegroundActivity()) { + incrementTimeUsed(timeStamp); + } + if (hasVisibleActivity()) { + incrementTimeVisible(timeStamp); } break; - case CONTINUE_PREVIOUS_DAY: - mLastTimeUsed = timeStamp; - mLastForegroundActivityEventMap.put(className, eventType); - break; - case FOREGROUND_SERVICE_STOP: case FOREGROUND_SERVICE_START: + case FOREGROUND_SERVICE_STOP: updateForegroundService(className, timeStamp, eventType); break; case ROLLOVER_FOREGROUND_SERVICE: - // ROLLOVER_FOREGROUND_SERVICE means updating all foreground services. - final int size2 = mLastForegroundServiceEventMap.size(); - for (int i = 0; i < size2; i++) { - final String name = mLastForegroundServiceEventMap.keyAt(i); - updateForegroundService(name, timeStamp, eventType); + // ROLLOVER_FOREGROUND_SERVICE updates all foreground services. + if (anyForegroundServiceStarted()) { + incrementServiceTimeUsed(timeStamp); } break; case CONTINUING_FOREGROUND_SERVICE: mLastTimeForegroundServiceUsed = timeStamp; - mLastForegroundServiceEventMap.put(className, eventType); + mForegroundServices.put(className, eventType); + break; + case FLUSH_TO_DISK: + // update usage of all active activities/services. + if (hasForegroundActivity()) { + incrementTimeUsed(timeStamp); + } + if (hasVisibleActivity()) { + incrementTimeVisible(timeStamp); + } + if (anyForegroundServiceStarted()) { + incrementServiceTimeUsed(timeStamp); + } break; default: break; } mEndTimeStamp = timeStamp; - if (eventType == MOVE_TO_FOREGROUND) { + if (eventType == ACTIVITY_RESUMED) { mLaunchCount += 1; } } @@ -455,8 +592,10 @@ public final class UsageStats implements Parcelable { dest.writeLong(mBeginTimeStamp); dest.writeLong(mEndTimeStamp); dest.writeLong(mLastTimeUsed); + dest.writeLong(mLastTimeVisible); dest.writeLong(mLastTimeForegroundServiceUsed); dest.writeLong(mTotalTimeInForeground); + dest.writeLong(mTotalTimeVisible); dest.writeLong(mTotalTimeForegroundServiceUsed); dest.writeInt(mLaunchCount); dest.writeInt(mAppLaunchCount); @@ -477,21 +616,26 @@ public final class UsageStats implements Parcelable { } dest.writeBundle(allCounts); - final Bundle foregroundActivityEventBundle = new Bundle(); - final int foregroundEventSize = mLastForegroundActivityEventMap.size(); - for (int i = 0; i < foregroundEventSize; i++) { - foregroundActivityEventBundle.putInt(mLastForegroundActivityEventMap.keyAt(i), - mLastForegroundActivityEventMap.valueAt(i)); + writeSparseIntArray(dest, mActivities); + dest.writeBundle(eventMapToBundle(mForegroundServices)); + } + + private void writeSparseIntArray(Parcel dest, SparseIntArray arr) { + final int size = arr.size(); + dest.writeInt(size); + for (int i = 0; i < size; i++) { + dest.writeInt(arr.keyAt(i)); + dest.writeInt(arr.valueAt(i)); } - dest.writeBundle(foregroundActivityEventBundle); + } - final Bundle foregroundServiceEventBundle = new Bundle(); - final int foregroundServiceEventSize = mLastForegroundServiceEventMap.size(); - for (int i = 0; i < foregroundServiceEventSize; i++) { - foregroundServiceEventBundle.putInt(mLastForegroundServiceEventMap.keyAt(i), - mLastForegroundServiceEventMap.valueAt(i)); + private Bundle eventMapToBundle(ArrayMap<String, Integer> eventMap) { + final Bundle bundle = new Bundle(); + final int size = eventMap.size(); + for (int i = 0; i < size; i++) { + bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i)); } - dest.writeBundle(foregroundServiceEventBundle); + return bundle; } public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() { @@ -502,8 +646,10 @@ public final class UsageStats implements Parcelable { stats.mBeginTimeStamp = in.readLong(); stats.mEndTimeStamp = in.readLong(); stats.mLastTimeUsed = in.readLong(); + stats.mLastTimeVisible = in.readLong(); stats.mLastTimeForegroundServiceUsed = in.readLong(); stats.mTotalTimeInForeground = in.readLong(); + stats.mTotalTimeVisible = in.readLong(); stats.mTotalTimeForegroundServiceUsed = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mAppLaunchCount = in.readInt(); @@ -527,12 +673,21 @@ public final class UsageStats implements Parcelable { } } } - readBundleToEventMap(stats.mLastForegroundActivityEventMap, in.readBundle()); - readBundleToEventMap(stats.mLastForegroundServiceEventMap, in.readBundle()); + readSparseIntArray(in, stats.mActivities); + readBundleToEventMap(in.readBundle(), stats.mForegroundServices); return stats; } - private void readBundleToEventMap(ArrayMap<String, Integer> eventMap, Bundle bundle) { + private void readSparseIntArray(Parcel in, SparseIntArray arr) { + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + final int key = in.readInt(); + final int value = in.readInt(); + arr.put(key, value); + } + } + + private void readBundleToEventMap(Bundle bundle, ArrayMap<String, Integer> eventMap) { if (bundle != null) { for (String className : bundle.keySet()) { final int event = bundle.getInt(className); diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 1a656ab39373..2edad350e18e 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -37,9 +37,12 @@ public abstract class UsageStatsManagerInternal { * @param component The component for which this event occurred. * @param userId The user id to which the component belongs to. * @param eventType The event that occurred. Valid values can be found at - * {@link UsageEvents} + * {@link UsageEvents} + * @param instanceId For activity, hashCode of ActivityRecord's appToken. + * For non-activity, it is not used. */ - public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType); + public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType, + int instanceId); /** * Reports an event to the UsageStatsManager. diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto index 3d60a86d86c9..528c1a4134f1 100644 --- a/core/proto/android/server/usagestatsservice.proto +++ b/core/proto/android/server/usagestatsservice.proto @@ -54,6 +54,9 @@ message IntervalStatsProto { // Time attributes stored as an offset of the IntervalStats's beginTime. optional int64 last_time_service_used_ms = 8; optional int64 total_time_service_used_ms = 9; + // Time attributes stored as an offset of the IntervalStats's beginTime. + optional int64 last_time_visible_ms = 10; + optional int64 total_time_visible_ms = 11; } // Stores the relevant information an IntervalStats will have about a Configuration @@ -82,6 +85,9 @@ message IntervalStatsProto { optional string notification_channel = 12; // notification_channel_index contains the index + 1 of the channel name in the string pool optional int32 notification_channel_index = 13; + // If class field is an Activity, instance_id is a unique id of the + // Activity object. + optional int32 instance_id = 14; } // The following fields contain supplemental data used to build IntervalStats, such as a string diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java index 1f047f9e6d10..28aaf1e05644 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java @@ -16,18 +16,22 @@ package android.app.usage; -import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY; +import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; import static android.app.usage.UsageEvents.Event.END_OF_DAY; +import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP; -import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND; -import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND; import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import android.app.usage.UsageEvents.Event; import android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -120,10 +124,10 @@ public class UsageStatsTest { left.mBeginTimeStamp = 100000; left.mTotalTimeInForeground = 10; - left.mLastForegroundActivityEventMap.put("com.test.activity1", MOVE_TO_FOREGROUND); - left.mLastForegroundActivityEventMap.put("com.test.activity2", MOVE_TO_FOREGROUND); - left.mLastForegroundServiceEventMap.put("com.test.service1", FOREGROUND_SERVICE_START); - left.mLastForegroundServiceEventMap.put("com.test.service2", FOREGROUND_SERVICE_START); + left.mActivities.put(1, Event.ACTIVITY_RESUMED); + left.mActivities.put(2, Event.ACTIVITY_RESUMED); + left.mForegroundServices.put("com.test.service1", FOREGROUND_SERVICE_START); + left.mForegroundServices.put("com.test.service2", FOREGROUND_SERVICE_START); Parcel p = Parcel.obtain(); left.writeToParcel(p, 0); @@ -133,131 +137,182 @@ public class UsageStatsTest { } @Test - public void testForegroundActivity() { + public void testActivity() { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND); + left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1); assertEquals(left.mLastTimeUsed, 200000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mLastTimeVisible, 200000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); assertEquals(left.mLaunchCount, 1); + assertEquals(left.mTotalTimeInForeground, 0); + assertEquals(left.mTotalTimeVisible, 0); - left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND); + left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1); assertEquals(left.mLastTimeUsed, 350000); - assertFalse(left.mLastForegroundActivityEventMap.containsKey("com.test.activity1")); + assertEquals(left.mLastTimeVisible, 350000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 350000 - 200000); + assertEquals(left.mTotalTimeVisible, 350000 - 200000); + + left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1); + assertEquals(left.mLastTimeUsed, 350000); + assertEquals(left.mLastTimeVisible, 400000); + assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED); + assertEquals(left.mTotalTimeInForeground, 350000 - 200000); + assertEquals(left.mTotalTimeVisible, 400000 - 200000); + + left.update("com.test.activity1", 500000, ACTIVITY_DESTROYED, 1); + assertEquals(left.mLastTimeUsed, 350000); + assertEquals(left.mLastTimeVisible, 400000); + assertTrue(left.mActivities.indexOfKey(1) < 0); + assertEquals(left.mTotalTimeInForeground, 350000 - 200000); + assertEquals(left.mTotalTimeVisible, 400000 - 200000); } @Test - public void testEvent_CONTINUE_PREVIOUS_DAY() { + public void testEvent_END_OF_DAY() { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY); + left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1); assertEquals(left.mLastTimeUsed, 100000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLaunchCount, 0); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); + assertEquals(left.mLaunchCount, 1); - left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND); + left.update(null, 350000, END_OF_DAY, 0); assertEquals(left.mLastTimeUsed, 350000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mLastTimeVisible, 350000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); assertEquals(left.mTotalTimeInForeground, 350000 - 100000); + assertEquals(left.mTotalTimeVisible, 350000 - 100000); } @Test - public void testEvent_END_OF_DAY() { + public void testEvent_ACTIVITY_PAUSED() { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY); - assertEquals(left.mLastTimeUsed, 100000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLaunchCount, 0); + left.update("com.test.activity1", 100000, ACTIVITY_PAUSED, 1); + assertEquals(left.mLastTimeUsed, 0); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); - left.update(null, 350000, END_OF_DAY); - assertEquals(left.mLastTimeUsed, 350000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(END_OF_DAY)); - assertEquals(left.mTotalTimeInForeground, 350000 - 100000); + left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1); + assertEquals(left.mLastTimeUsed, 200000); + assertEquals(left.mLastTimeVisible, 200000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); + assertEquals(left.mTotalTimeInForeground, 0); + assertEquals(left.mTotalTimeVisible, 200000 - 100000); + + left.update("com.test.activity1", 300000, ACTIVITY_PAUSED, 1); + assertEquals(left.mLastTimeUsed, 300000); + assertEquals(left.mLastTimeVisible, 300000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); + assertEquals(left.mTotalTimeInForeground, 300000 - 200000); + assertEquals(left.mTotalTimeVisible, 300000 - 100000); + + left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1); + assertEquals(left.mLastTimeUsed, 300000); + assertEquals(left.mLastTimeVisible, 400000); + assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED); + assertEquals(left.mTotalTimeInForeground, 300000 - 200000); + assertEquals(left.mTotalTimeVisible, 400000 - 100000); } @Test - public void testForegroundActivityEventSequence() { + public void testEvent_CHANGE_TO_INVISIBLE() { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY); + left.update("com.test.activity1", 100000, ACTIVITY_RESUMED, 1); assertEquals(left.mLastTimeUsed, 100000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLaunchCount, 0); - - left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND); - assertEquals(left.mLastTimeUsed, 350000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); - assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED); - left.update("com.test.activity1", 450000, MOVE_TO_FOREGROUND); - assertEquals(left.mLastTimeUsed, 450000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(MOVE_TO_FOREGROUND)); - assertEquals(left.mTotalTimeInForeground, 250000); - - left.update("com.test.activity1", 500000, MOVE_TO_BACKGROUND); - assertEquals(left.mLastTimeUsed, 500000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); - assertEquals(left.mTotalTimeInForeground, 250000 + 50000 /*500000 - 450000*/); + left.update("com.test.activity1", 200000, ACTIVITY_STOPPED, 1); + assertEquals(left.mLastTimeUsed, 200000); + assertEquals(left.mLastTimeVisible, 200000); + assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED); + assertEquals(left.mTotalTimeInForeground, 200000 - 100000); + assertEquals(left.mTotalTimeVisible, 200000 - 100000); + + left.update("com.test.activity1", 300000, ACTIVITY_RESUMED, 1); + assertEquals(left.mLastTimeUsed, 300000); + assertEquals(left.mLastTimeVisible, 300000); + assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED); + assertEquals(left.mTotalTimeInForeground, 200000 - 100000); + assertEquals(left.mTotalTimeVisible, 200000 - 100000); } @Test - public void testForegroundActivityEventOutOfSequence() { + public void testEvent_ACTIVITY_DESTROYED() { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY); + left.update("com.test.activity1", 100000, ACTIVITY_RESUMED, 1); assertEquals(left.mLastTimeUsed, 100000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLaunchCount, 0); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), ACTIVITY_RESUMED); - left.update("com.test.activity1", 150000, MOVE_TO_FOREGROUND); - assertEquals(left.mLastTimeUsed, 150000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(MOVE_TO_FOREGROUND)); + left.update("com.test.activity1", 200000, ACTIVITY_DESTROYED, 1); + assertEquals(left.mLastTimeUsed, 200000); + assertEquals(left.mLastTimeVisible, 200000); + assertTrue(left.mActivities.indexOfKey(1) < 0); + assertEquals(left.mTotalTimeInForeground, 200000 - 100000); + assertEquals(left.mTotalTimeVisible, 200000 - 100000); + } + + @Test + public void testActivityEventOutOfOrder() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1); + assertEquals(left.mLastTimeUsed, 100000); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); assertEquals(left.mLaunchCount, 1); - assertEquals(left.mTotalTimeInForeground, 50000 /*150000 - 100000*/); + assertEquals(left.mTotalTimeInForeground, 0); + assertEquals(left.mTotalTimeVisible, 0); - left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND); + left.update("com.test.activity1", 200000, Event.ACTIVITY_RESUMED, 1); assertEquals(left.mLastTimeUsed, 200000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mLastTimeVisible, 200000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); assertEquals(left.mLaunchCount, 2); assertEquals(left.mTotalTimeInForeground, 100000); + assertEquals(left.mTotalTimeVisible, 100000 /*200000 - 100000*/); - left.update("com.test.activity1", 250000, MOVE_TO_BACKGROUND); + left.update("com.test.activity1", 250000, ACTIVITY_PAUSED, 1); assertEquals(left.mLastTimeUsed, 250000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mLastTimeVisible, 250000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 150000); + assertEquals(left.mTotalTimeVisible, 150000 /*250000 - 100000*/); - left.update("com.test.activity1", 300000, MOVE_TO_BACKGROUND); + left.update("com.test.activity1", 300000, ACTIVITY_PAUSED, 1); assertEquals(left.mLastTimeUsed, 250000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mLastTimeVisible, 300000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 150000); + assertEquals(left.mTotalTimeVisible, 200000 /*300000 - 100000*/); - left.update("com.test.activity1", 350000, MOVE_TO_FOREGROUND); + left.update("com.test.activity1", 350000, Event.ACTIVITY_RESUMED, 1); assertEquals(left.mLastTimeUsed, 350000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mLastTimeVisible, 350000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); assertEquals(left.mTotalTimeInForeground, 150000); + assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/); - left.update("com.test.activity1", 400000, END_OF_DAY); + left.update("com.test.activity1", 400000, END_OF_DAY, 1); assertEquals(left.mLastTimeUsed, 400000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(END_OF_DAY)); + assertEquals(left.mLastTimeVisible, 400000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); assertEquals(left.mTotalTimeInForeground, 200000); + assertEquals(left.mTotalTimeVisible, 300000 /*400000 - 100000*/); } @Test @@ -265,28 +320,41 @@ public class UsageStatsTest { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY); - left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY); + left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1); + left.update("com.test.activity2", 100000, Event.ACTIVITY_RESUMED, 2); assertEquals(left.mLastTimeUsed, 100000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLaunchCount, 0); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); + assertEquals(left.mActivities.get(2), Event.ACTIVITY_RESUMED); + assertEquals(left.mLaunchCount, 2); - left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND); + left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1); assertEquals(left.mLastTimeUsed, 350000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mLastTimeVisible, 350000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/); + assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/); - left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND); + left.update("com.test.activity2", 450000, ACTIVITY_PAUSED, 2); assertEquals(left.mLastTimeUsed, 450000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null); + assertEquals(left.mLastTimeVisible, 450000); + assertEquals(left.mActivities.get(2), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/); + assertEquals(left.mTotalTimeVisible, 250000 + 100000 /*450000 - 350000*/); + + left.update("com.test.activity1", 550000, ACTIVITY_STOPPED, 1); + assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mLastTimeVisible, 550000); + assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED); + assertEquals(left.mTotalTimeInForeground, 350000); + assertEquals(left.mTotalTimeVisible, 350000 + 100000 /*550000 - 450000*/); - left.update(null, 500000, END_OF_DAY); + left.update("com.test.activity2", 650000, ACTIVITY_STOPPED, 2); assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mLastTimeVisible, 650000); + assertEquals(left.mActivities.get(2), ACTIVITY_STOPPED); assertEquals(left.mTotalTimeInForeground, 350000); + assertEquals(left.mTotalTimeVisible, 450000 + 100000 /*650000 - 550000*/); } @Test @@ -294,15 +362,14 @@ public class UsageStatsTest { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START); + left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 200000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(FOREGROUND_SERVICE_START)); - assertEquals(left.mLaunchCount, 0); - left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 350000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mForegroundServices.get("com.test.service1"), null); assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 200000); } @@ -311,15 +378,15 @@ public class UsageStatsTest { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.service1", 100000, CONTINUING_FOREGROUND_SERVICE); + left.update("com.test.service1", 100000, + CONTINUING_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 100000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(CONTINUING_FOREGROUND_SERVICE)); - assertEquals(left.mLaunchCount, 0); - left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 350000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mForegroundServices.get("com.test.service1"), null); assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000); } @@ -329,16 +396,15 @@ public class UsageStatsTest { left.mBeginTimeStamp = 100000; left.update("com.test.service1", 100000, - CONTINUING_FOREGROUND_SERVICE); + CONTINUING_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 100000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(CONTINUING_FOREGROUND_SERVICE)); - assertEquals(left.mLaunchCount, 0); - left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE); + left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 350000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), - new Integer(ROLLOVER_FOREGROUND_SERVICE)); + assertEquals(left.mForegroundServices.get("com.test.service1"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000); } @@ -348,27 +414,28 @@ public class UsageStatsTest { left.mBeginTimeStamp = 100000; left.update("com.test.service1", 100000, - CONTINUING_FOREGROUND_SERVICE); + CONTINUING_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 100000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(CONTINUING_FOREGROUND_SERVICE)); assertEquals(left.mLaunchCount, 0); - left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 350000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mForegroundServices.get("com.test.service1"), null); assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/); - left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START); + left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 450000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(FOREGROUND_SERVICE_START)); assertEquals(left.mTotalTimeForegroundServiceUsed, 250000); - left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 500000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); - assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 50000 /*500000 - 450000*/); + assertEquals(left.mForegroundServices.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, + 250000 + 50000 /*500000 - 450000*/); } @Test @@ -377,27 +444,27 @@ public class UsageStatsTest { left.mBeginTimeStamp = 100000; left.update("com.test.service1", 100000, - CONTINUING_FOREGROUND_SERVICE); + CONTINUING_FOREGROUND_SERVICE, 0); left.update("com.test.service2", 100000, - CONTINUING_FOREGROUND_SERVICE); + CONTINUING_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 100000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(CONTINUING_FOREGROUND_SERVICE)); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), + assertEquals(left.mForegroundServices.get("com.test.service2"), new Integer(CONTINUING_FOREGROUND_SERVICE)); - assertEquals(left.mLaunchCount, 0); - left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 350000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mForegroundServices.get("com.test.service1"), null); assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/); - left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 450000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null); - assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 100000 /*450000 - 350000*/); + assertEquals(left.mForegroundServices.get("com.test.service2"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, + 250000 + 100000 /*450000 - 350000*/); - left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE); + left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 450000); assertEquals(left.mTotalTimeForegroundServiceUsed, 350000); } @@ -407,76 +474,117 @@ public class UsageStatsTest { left.mPackageName = "com.test"; left.mBeginTimeStamp = 100000; - left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY); - left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY); + left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1); + left.update("com.test.activity2", 100000, Event.ACTIVITY_RESUMED, 2); left.update("com.test.service1", 100000, - CONTINUING_FOREGROUND_SERVICE); + CONTINUING_FOREGROUND_SERVICE, 0); left.update("com.test.service2", 100000, - CONTINUING_FOREGROUND_SERVICE); + CONTINUING_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeUsed, 100000); assertEquals(left.mLastTimeForegroundServiceUsed, 100000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), - new Integer(CONTINUE_PREVIOUS_DAY)); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); + assertEquals(left.mActivities.get(2), Event.ACTIVITY_RESUMED); + assertEquals(left.mForegroundServices.get("com.test.service1"), new Integer(CONTINUING_FOREGROUND_SERVICE)); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), + assertEquals(left.mForegroundServices.get("com.test.service2"), new Integer(CONTINUING_FOREGROUND_SERVICE)); - assertEquals(left.mLaunchCount, 0); + assertEquals(left.mLaunchCount, 2); - left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND); + left.update("com.test.activity1", 350000, ACTIVITY_PAUSED, 1); assertEquals(left.mLastTimeUsed, 350000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mLastTimeVisible, 350000); + assertEquals(left.mActivities.get(1), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/); + assertEquals(left.mTotalTimeVisible, 250000 /*350000 - 100000*/); - left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 400000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mForegroundServices.get("com.test.service1"), null); assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 /*400000 - 100000*/); - left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND); + left.update("com.test.activity2", 450000, ACTIVITY_PAUSED, 2); assertEquals(left.mLastTimeUsed, 450000); - assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null); + assertEquals(left.mLastTimeVisible, 450000); + assertEquals(left.mActivities.get(2), ACTIVITY_PAUSED); assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/); + assertEquals(left.mTotalTimeVisible, 250000 + 100000 /*450000 - 350000*/); - left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP); + left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 500000); - assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null); - assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 + 100000 /*500000 - 400000*/); + assertEquals(left.mForegroundServices.get("com.test.service2"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, + 300000 + 100000 /*500000 - 400000*/); - left.update(null, 550000, END_OF_DAY); + left.update(null, 550000, END_OF_DAY, 0); assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mLastTimeVisible, 550000); assertEquals(left.mTotalTimeInForeground, 350000); - left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE); + assertEquals(left.mTotalTimeVisible, 450000); + + left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE, 0); assertEquals(left.mLastTimeForegroundServiceUsed, 500000); assertEquals(left.mTotalTimeForegroundServiceUsed, 400000); } + @Test + public void testEvent_FLUSH_TO_DISK() { + testClosingEvent(FLUSH_TO_DISK); + } + + private void testClosingEvent(int eventType) { + // When these three closing events are received, all open activities/services need to be + // closed and usage stats are updated. + if (eventType != FLUSH_TO_DISK) { + fail("Closing eventType must be one of FLUSH_TO_DISK"); + } + + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.activity1", 100000, Event.ACTIVITY_RESUMED, 1); + assertEquals(left.mLastTimeUsed, 100000); + assertEquals(left.mLastTimeVisible, 100000); + assertEquals(left.mActivities.get(1), Event.ACTIVITY_RESUMED); + + left.update("com.test.service1", 150000, FOREGROUND_SERVICE_START, 0); + assertEquals(left.mLastTimeForegroundServiceUsed, 150000); + assertEquals(left.mForegroundServices.get("com.test.service1"), + new Integer(FOREGROUND_SERVICE_START)); + + left.update(null, 200000, eventType, 0); + assertEquals(left.mLastTimeUsed, 200000); + assertEquals(left.mLastTimeVisible, 200000); + assertEquals(left.mTotalTimeInForeground, 200000 - 100000); + assertEquals(left.mTotalTimeVisible, 200000 - 100000); + assertEquals(left.mLastTimeForegroundServiceUsed, 200000); + assertEquals(left.mTotalTimeForegroundServiceUsed, 200000 - 150000); + } + void compareUsageStats(UsageStats us1, UsageStats us2) { assertEquals(us1.mPackageName, us2.mPackageName); assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp); assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); + assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible); assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed); assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground); assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed); assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount); - assertEquals(us1.mLastForegroundActivityEventMap.size(), - us2.mLastForegroundActivityEventMap.size()); - for (int i = 0; i < us1.mLastForegroundActivityEventMap.size(); i++) { - assertEquals(us1.mLastForegroundActivityEventMap.keyAt(i), - us2.mLastForegroundActivityEventMap.keyAt(i)); - assertEquals(us1.mLastForegroundActivityEventMap.valueAt(i), - us2.mLastForegroundActivityEventMap.valueAt(i)); + assertEquals(us1.mActivities.size(), + us2.mActivities.size()); + for (int i = 0; i < us1.mActivities.size(); i++) { + assertEquals(us1.mActivities.keyAt(i), + us2.mActivities.keyAt(i)); + assertEquals(us1.mActivities.valueAt(i), + us2.mActivities.valueAt(i)); } - assertEquals(us1.mLastForegroundServiceEventMap.size(), - us2.mLastForegroundServiceEventMap.size()); - for (int i = 0; i < us1.mLastForegroundServiceEventMap.size(); i++) { - assertEquals(us1.mLastForegroundServiceEventMap.keyAt(i), - us2.mLastForegroundServiceEventMap.keyAt(i)); - assertEquals(us1.mLastForegroundServiceEventMap.valueAt(i), - us2.mLastForegroundServiceEventMap.valueAt(i)); + assertEquals(us1.mForegroundServices.size(), + us2.mForegroundServices.size()); + for (int i = 0; i < us1.mForegroundServices.size(); i++) { + assertEquals(us1.mForegroundServices.keyAt(i), + us2.mForegroundServices.keyAt(i)); + assertEquals(us1.mForegroundServices.valueAt(i), + us2.mForegroundServices.valueAt(i)); } assertEquals(us1.mChooserCounts, us2.mChooserCounts); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 40da881c13fe..57bf4a3b03be 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2724,47 +2724,86 @@ public class ActivityManagerService extends IActivityManager.Stub return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0; } - void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) { + /** + * Update battery stats on the activity' usage. + * @param activity + * @param uid + * @param userId + * @param resumed + */ + void updateBatteryStats(ComponentName activity, int uid, int userId, boolean resumed) { if (DEBUG_SWITCH) { Slog.d(TAG_SWITCH, - "updateUsageStats: comp=" + activity + "res=" + resumed); + "updateBatteryStats: comp=" + activity + "res=" + resumed); } final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED, - uid, activity.getPackageName(), - activity.getShortClassName(), resumed ? - StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND : + uid, activity.getPackageName(), activity.getShortClassName(), + resumed ? StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND : StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND); - if (resumed) { - if (mUsageStatsService != null) { - mUsageStatsService.reportEvent(activity, userId, - UsageEvents.Event.MOVE_TO_FOREGROUND); - - } - synchronized (stats) { + synchronized (stats) { + if (resumed) { stats.noteActivityResumedLocked(uid); + } else { + stats.noteActivityPausedLocked(uid); } - } else { + } + } + + /** + * Update UsageStas on the activity's usage. + * @param activity + * @param userId + * @param event + * @param appToken ActivityRecord's appToken. + */ + public void updateActivityUsageStats(ComponentName activity, int userId, int event, + IBinder appToken) { + if (DEBUG_SWITCH) { + Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp=" + + activity + " hash=" + appToken.hashCode() + " event=" + event); + } + synchronized (this) { if (mUsageStatsService != null) { - mUsageStatsService.reportEvent(activity, userId, - UsageEvents.Event.MOVE_TO_BACKGROUND); + mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode()); } - synchronized (stats) { - stats.noteActivityPausedLocked(uid); + } + } + + /** + * Update UsageStats on this package's usage. + * @param packageName + * @param userId + * @param event + */ + public void updateActivityUsageStats(String packageName, int userId, int event) { + if (DEBUG_SWITCH) { + Slog.d(TAG_SWITCH, "updateActivityUsageStats: package=" + + packageName + " event=" + event); + } + synchronized (this) { + if (mUsageStatsService != null) { + mUsageStatsService.reportEvent(packageName, userId, event); } } } + /** + * Update Usages on this foreground service's usage. + * @param service + * @param userId + * @param started + */ void updateForegroundServiceUsageStats(ComponentName service, int userId, boolean started) { if (DEBUG_SWITCH) { Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp=" - + service + "started=" + started); + + service + " started=" + started); } synchronized (this) { if (mUsageStatsService != null) { mUsageStatsService.reportEvent(service, userId, started ? UsageEvents.Event.FOREGROUND_SERVICE_START - : UsageEvents.Event.FOREGROUND_SERVICE_STOP); + : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0); } } } @@ -19041,9 +19080,19 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) { + public void updateBatteryStats(ComponentName activity, int uid, int userId, + boolean resumed) { + synchronized (ActivityManagerService.this) { + ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed); + } + } + + @Override + public void updateActivityUsageStats(ComponentName activity, int userId, int event, + IBinder appToken) { synchronized (ActivityManagerService.this) { - ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed); + ActivityManagerService.this.updateActivityUsageStats(activity, userId, event, + appToken); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4e9c5ab39ea8..2bc0edb1dea0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -96,6 +96,7 @@ import static com.android.server.am.ActivityRecordProto.TRANSLUCENT; import static com.android.server.am.ActivityRecordProto.VISIBLE; import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY; import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY; +import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; @@ -122,8 +123,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; -import static com.android.server.wm.ActivityTaskManagerService - .RELAUNCH_REASON_WINDOWING_MODE_RESIZE; +import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; @@ -157,6 +157,7 @@ import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.PipModeChangeItem; import android.app.servertransaction.ResumeActivityItem; import android.app.servertransaction.WindowVisibilityItem; +import android.app.usage.UsageEvents.Event; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -1801,6 +1802,18 @@ final class ActivityRecord extends ConfigurationContainer { } mAppWindowToken.detachChildren(); } + + if (state == RESUMED) { + mAtmService.updateBatteryStats(this, true); + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED); + } else if (state == PAUSED) { + mAtmService.updateBatteryStats(this, false); + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED); + } else if (state == STOPPED) { + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED); + } else if (state == DESTROYED) { + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED); + } } ActivityState getState() { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 7683172815e9..94a5b27fb303 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1617,7 +1617,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai try { EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving); - mService.updateUsageStats(prev, false); mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, @@ -4649,9 +4648,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.mUserId, System.identityHashCode(r), r.getTaskRecord().taskId, r.shortComponentName, "proc died without state saved"); - if (r.getState() == RESUMED) { - mService.updateUsageStats(r, false); - } } } else { // We have the current state for this activity, so diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index e761ad86c770..234dba8a4ba0 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2040,9 +2040,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mStoppingActivities.remove(r); final ActivityStack stack = r.getActivityStack(); - if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { - mService.updateUsageStats(r, true); - } if (stack.getDisplay().allResumedActivitiesComplete()) { mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); // Make sure activity & window visibility should be identical diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 182d1a0f9c5d..46ee08f740b0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5234,13 +5234,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.post(mAmInternal::updateCpuStats); } - void updateUsageStats(ActivityRecord component, boolean resumed) { - final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats, + void updateBatteryStats(ActivityRecord component, boolean resumed) { + final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateBatteryStats, mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId, resumed); mH.sendMessage(m); } + void updateActivityUsageStats(ActivityRecord activity, int event) { + final Message m = PooledLambda.obtainMessage( + ActivityManagerInternal::updateActivityUsageStats, mAmInternal, + activity.mActivityComponent, activity.mUserId, event, activity.appToken); + mH.sendMessage(m); + } + void setBooting(boolean booting) { mAmInternal.setBooting(booting); } diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index bf7f53dd7d8f..860656bf47f4 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -16,6 +16,8 @@ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE; + import static junit.framework.TestCase.fail; import static org.testng.Assert.assertEquals; @@ -136,9 +138,11 @@ public class UsageStatsDatabaseTest { event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP; } - event.mClass = ".fake.class.name" + i % 11; + final int instanceId = i % 11; + event.mClass = ".fake.class.name" + instanceId; event.mTimeStamp = time; - event.mEventType = i % 19; //"random" event type + event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type + event.mInstanceId = instanceId; switch (event.mEventType) { case Event.CONFIGURATION_CHANGE: @@ -160,7 +164,8 @@ public class UsageStatsDatabaseTest { } mIntervalStats.events.insert(event); - mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType); + mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType, + event.mInstanceId); time += timeProgression; // Arbitrary progression of time } @@ -219,7 +224,9 @@ public class UsageStatsDatabaseTest { // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); + assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible); assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground); + assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible); assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed); assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed); // mLaunchCount not persisted, so skipped diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index f553c35071a4..2c6cb28e2244 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -447,7 +447,11 @@ class ActivityTestsBase { } @Override - void updateUsageStats(ActivityRecord component, boolean resumed) { + void updateBatteryStats(ActivityRecord component, boolean resumed) { + } + + @Override + void updateActivityUsageStats(ActivityRecord activity, int event) { } @Override diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 4f573a475ae7..65ed85db17bd 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -842,8 +842,8 @@ public class AppStandbyController { final boolean previouslyIdle = mAppIdleHistory.isIdle( event.mPackage, userId, elapsedRealtime); // Inform listeners if necessary - if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND - || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND + if ((event.mEventType == UsageEvents.Event.ACTIVITY_RESUMED + || event.mEventType == UsageEvents.Event.ACTIVITY_PAUSED || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION || event.mEventType == UsageEvents.Event.USER_INTERACTION || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN @@ -894,8 +894,8 @@ public class AppStandbyController { private int usageEventToSubReason(int eventType) { switch (eventType) { - case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND; - case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND; + case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND; + case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND; case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION; case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION; case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN; diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 84052672f6d3..94cc6502a12d 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -15,10 +15,31 @@ */ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; +import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; +import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE; +import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY; +import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.END_OF_DAY; +import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; +import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; +import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP; +import static android.app.usage.UsageEvents.Event.KEYGUARD_HIDDEN; +import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN; +import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION; +import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE; +import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE; +import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION; +import static android.app.usage.UsageEvents.Event.STANDBY_BUCKET_CHANGED; +import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION; + import android.app.usage.ConfigurationStats; import android.app.usage.EventList; import android.app.usage.EventStats; -import android.app.usage.UsageEvents; +import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.content.res.Configuration; import android.util.ArrayMap; @@ -125,8 +146,8 @@ public class IntervalStats { /** * Builds a UsageEvents.Event, but does not add it internally. */ - UsageEvents.Event buildEvent(String packageName, String className) { - UsageEvents.Event event = new UsageEvents.Event(); + Event buildEvent(String packageName, String className) { + Event event = new Event(); event.mPackage = getCachedStringRef(packageName); if (className != null) { event.mClass = getCachedStringRef(className); @@ -138,9 +159,9 @@ public class IntervalStats { * Builds a UsageEvents.Event from a proto, but does not add it internally. * Built here to take advantage of the cached String Refs */ - UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool) + Event buildEvent(ProtoInputStream parser, List<String> stringPool) throws IOException { - final UsageEvents.Event event = new UsageEvents.Event(); + final Event event = new Event(); while (true) { switch (parser.nextField()) { case (int) IntervalStatsProto.Event.PACKAGE: @@ -190,20 +211,23 @@ public class IntervalStats { parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX) - 1)); break; + case (int) IntervalStatsProto.Event.INSTANCE_ID: + event.mInstanceId = parser.readInt(IntervalStatsProto.Event.INSTANCE_ID); + break; case ProtoInputStream.NO_MORE_FIELDS: // Handle default values for certain events types switch (event.mEventType) { - case UsageEvents.Event.CONFIGURATION_CHANGE: + case CONFIGURATION_CHANGE: if (event.mConfiguration == null) { event.mConfiguration = new Configuration(); } break; - case UsageEvents.Event.SHORTCUT_INVOCATION: + case SHORTCUT_INVOCATION: if (event.mShortcutId == null) { event.mShortcutId = ""; } break; - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + case NOTIFICATION_INTERRUPTION: if (event.mNotificationChannelId == null) { event.mNotificationChannelId = ""; } @@ -220,14 +244,15 @@ public class IntervalStats { private boolean isStatefulEvent(int eventType) { switch (eventType) { - case UsageEvents.Event.MOVE_TO_FOREGROUND: - case UsageEvents.Event.MOVE_TO_BACKGROUND: - case UsageEvents.Event.FOREGROUND_SERVICE_START: - case UsageEvents.Event.FOREGROUND_SERVICE_STOP: - case UsageEvents.Event.END_OF_DAY: - case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE: - case UsageEvents.Event.CONTINUE_PREVIOUS_DAY: - case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE: + case ACTIVITY_RESUMED: + case ACTIVITY_PAUSED: + case ACTIVITY_STOPPED: + case FOREGROUND_SERVICE_START: + case FOREGROUND_SERVICE_STOP: + case END_OF_DAY: + case ROLLOVER_FOREGROUND_SERVICE: + case CONTINUE_PREVIOUS_DAY: + case CONTINUING_FOREGROUND_SERVICE: return true; } return false; @@ -238,17 +263,56 @@ public class IntervalStats { * interaction. Excludes those that are internally generated. */ private boolean isUserVisibleEvent(int eventType) { - return eventType != UsageEvents.Event.SYSTEM_INTERACTION - && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED; + return eventType != SYSTEM_INTERACTION + && eventType != STANDBY_BUCKET_CHANGED; } /** + * Update the IntervalStats by a activity or foreground service event. + * @param packageName package name of this event. Is null if event targets to all packages. + * @param className class name of a activity or foreground service, could be null to if this + * is sent to all activities/services in this package. + * @param timeStamp Epoch timestamp in milliseconds. + * @param eventType event type as in {@link Event} + * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken. + * if className is not an activity, instanceId is not used. * @hide */ @VisibleForTesting - public void update(String packageName, String className, long timeStamp, int eventType) { - UsageStats usageStats = getOrCreateUsageStats(packageName); - usageStats.update(className, timeStamp, eventType); + public void update(String packageName, String className, long timeStamp, int eventType, + int instanceId) { + if (eventType == FLUSH_TO_DISK) { + // FLUSH_TO_DISK are sent to all packages. + final int size = packageStats.size(); + for (int i = 0; i < size; i++) { + UsageStats usageStats = packageStats.valueAt(i); + usageStats.update(null, timeStamp, eventType, instanceId); + } + } else if (eventType == ACTIVITY_DESTROYED) { + UsageStats usageStats = packageStats.get(packageName); + if (usageStats != null) { + // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED + // to ACTIVITY_STOPPED and add to event list. + // Otherwise do not add anything to event list. (Because we want to save space + // and we do not want a ACTIVITY_STOPPED followed by + // ACTIVITY_DESTROYED in event list). + final int index = usageStats.mActivities.indexOfKey(instanceId); + if (index >= 0) { + final int type = usageStats.mActivities.valueAt(index); + if (type != ACTIVITY_STOPPED) { + Event event = new Event(ACTIVITY_STOPPED, timeStamp); + event.mPackage = packageName; + event.mClass = className; + event.mInstanceId = instanceId; + addEvent(event); + } + } + usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId); + } + } else { + UsageStats usageStats = getOrCreateUsageStats(packageName); + usageStats.update(className, timeStamp, eventType, instanceId); + } endTime = timeStamp; } @@ -256,7 +320,7 @@ public class IntervalStats { * @hide */ @VisibleForTesting - public void addEvent(UsageEvents.Event event) { + public void addEvent(Event event) { if (events == null) { events = new EventList(); } @@ -265,7 +329,7 @@ public class IntervalStats { if (event.mClass != null) { event.mClass = getCachedStringRef(event.mClass); } - if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) { + if (event.mEventType == NOTIFICATION_INTERRUPTION) { event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId); } events.insert(event); @@ -338,13 +402,13 @@ public class IntervalStats { } void addEventStatsTo(List<EventStats> out) { - interactiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE, + interactiveTracker.addToEventStats(out, SCREEN_INTERACTIVE, beginTime, endTime); - nonInteractiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE, + nonInteractiveTracker.addToEventStats(out, SCREEN_NON_INTERACTIVE, beginTime, endTime); - keyguardShownTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_SHOWN, + keyguardShownTracker.addToEventStats(out, KEYGUARD_SHOWN, beginTime, endTime); - keyguardHiddenTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_HIDDEN, + keyguardHiddenTracker.addToEventStats(out, KEYGUARD_HIDDEN, beginTime, endTime); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java index 8e1c06091605..d70653781eff 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProto.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java @@ -135,6 +135,15 @@ final class UsageStatsProto { stats.mTotalTimeForegroundServiceUsed = proto.readLong( IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS); break; + case (int) IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS: + // Time attributes stored is an offset of the beginTime. + stats.mLastTimeVisible = statsOut.beginTime + proto.readLong( + IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS); + break; + case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS: + stats.mTotalTimeVisible = proto.readLong( + IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS); + break; } } if (stats.mLastTimeUsed == 0) { @@ -337,6 +346,11 @@ final class UsageStatsProto { usageStats.mLastTimeForegroundServiceUsed - stats.beginTime); proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS, usageStats.mTotalTimeForegroundServiceUsed); + // Time attributes stored as an offset of the beginTime. + proto.write(IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS, + usageStats.mLastTimeVisible - stats.beginTime); + proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS, + usageStats.mTotalTimeVisible); proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount); writeChooserCounts(proto, usageStats); proto.end(token); @@ -427,6 +441,7 @@ final class UsageStatsProto { proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime); proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags); proto.write(IntervalStatsProto.Event.TYPE, event.mEventType); + proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId); switch (event.mEventType) { case UsageEvents.Event.CONFIGURATION_CHANGE: if (event.mConfiguration != null) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 262125212c14..57dc08fcd253 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -16,6 +16,12 @@ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION; +import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE; +import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; +import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION; +import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION; + import android.Manifest; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -28,10 +34,10 @@ import android.app.usage.ConfigurationStats; import android.app.usage.EventStats; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; -import android.app.usage.UsageStatsManager; -import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -75,9 +81,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; /** * A service that collects, aggregates, and persists application usage data. @@ -106,6 +110,7 @@ public class UsageStatsService extends SystemService implements static final int MSG_FLUSH_TO_DISK = 1; static final int MSG_REMOVE_USER = 2; static final int MSG_UID_STATE_CHANGED = 3; + static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4; private final Object mLock = new Object(); Handler mHandler; @@ -135,12 +140,10 @@ public class UsageStatsService extends SystemService implements @Override public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket, int reason) { - Event event = new Event(); - event.mEventType = Event.STANDBY_BUCKET_CHANGED; + Event event = new Event(Event.STANDBY_BUCKET_CHANGED, + SystemClock.elapsedRealtime()); event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF); event.mPackage = packageName; - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @@ -397,7 +400,7 @@ public class UsageStatsService extends SystemService implements * Assuming the event's timestamp is measured in milliseconds since boot, * convert it to a system wall time. */ - private void convertToSystemTimeLocked(UsageEvents.Event event) { + private void convertToSystemTimeLocked(Event event) { event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot; } @@ -406,7 +409,6 @@ public class UsageStatsService extends SystemService implements */ void shutdown() { synchronized (mLock) { - mHandler.removeMessages(MSG_REPORT_EVENT); flushToDiskLocked(); } } @@ -414,7 +416,7 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - void reportEvent(UsageEvents.Event event, int userId) { + void reportEvent(Event event, int userId) { synchronized (mLock) { final long timeNow = checkAndGetTimeLocked(); final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -431,14 +433,14 @@ public class UsageStatsService extends SystemService implements mAppStandby.reportEvent(event, elapsedRealtime, userId); switch (event.mEventType) { - case Event.MOVE_TO_FOREGROUND: + case Event.ACTIVITY_RESUMED: try { mAppTimeLimit.noteUsageStart(event.getPackageName(), userId); } catch (IllegalArgumentException iae) { Slog.e(TAG, "Failed to note usage start", iae); } break; - case Event.MOVE_TO_BACKGROUND: + case Event.ACTIVITY_PAUSED: try { mAppTimeLimit.noteUsageStop(event.getPackageName(), userId); } catch (IllegalArgumentException iae) { @@ -450,10 +452,31 @@ public class UsageStatsService extends SystemService implements } /** - * Called by the Binder stub. + * Some events like FLUSH_TO_DISK need to be sent to all userId. + * @param event + */ + void reportEventToAllUserId(Event event) { + synchronized (mLock) { + final int userCount = mUserState.size(); + for (int i = 0; i < userCount; i++) { + Event copy = new Event(event); + reportEvent(copy, mUserState.keyAt(i)); + } + } + } + + /** + * Called by the Handler for message MSG_FLUSH_TO_DISK. */ void flushToDisk() { synchronized (mLock) { + // Before flush to disk, report FLUSH_TO_DISK event to signal UsageStats to update app + // usage. In case of abrupt power shutdown like battery drain or cold temperature, + // all UsageStats has correct data up to last flush to disk. + // The FLUSH_TO_DISK event is an internal event, it will not show up in IntervalStats' + // EventList. + Event event = new Event(FLUSH_TO_DISK, SystemClock.elapsedRealtime()); + reportEventToAllUserId(event); flushToDiskLocked(); } } @@ -656,9 +679,11 @@ public class UsageStatsService extends SystemService implements public void handleMessage(Message msg) { switch (msg.what) { case MSG_REPORT_EVENT: - reportEvent((UsageEvents.Event) msg.obj, msg.arg1); + reportEvent((Event) msg.obj, msg.arg1); + break; + case MSG_REPORT_EVENT_TO_ALL_USERID: + reportEventToAllUserId((Event) msg.obj); break; - case MSG_FLUSH_TO_DISK: flushToDisk(); break; @@ -1120,20 +1145,11 @@ public class UsageStatsService extends SystemService implements return; } - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(CHOOSER_ACTION, SystemClock.elapsedRealtime()); event.mPackage = packageName; - - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); - - event.mEventType = Event.CHOOSER_ACTION; - event.mAction = action; - event.mContentType = contentType; - event.mContentAnnotations = annotations; - mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @@ -1251,37 +1267,29 @@ public class UsageStatsService extends SystemService implements private final class LocalService extends UsageStatsManagerInternal { @Override - public void reportEvent(ComponentName component, int userId, int eventType) { + public void reportEvent(ComponentName component, int userId, int eventType, + int instanceId) { if (component == null) { Slog.w(TAG, "Event reported without a component name"); return; } - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(eventType, SystemClock.elapsedRealtime()); event.mPackage = component.getPackageName(); event.mClass = component.getClassName(); - - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); - - event.mEventType = eventType; + event.mInstanceId = instanceId; mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @Override public void reportEvent(String packageName, int userId, int eventType) { if (packageName == null) { - Slog.w(TAG, "Event reported without a package name"); + Slog.w(TAG, "Event reported without a package name, eventType:" + eventType); return; } - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(eventType, SystemClock.elapsedRealtime()); event.mPackage = packageName; - - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); - - event.mEventType = eventType; mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @@ -1292,13 +1300,8 @@ public class UsageStatsService extends SystemService implements return; } - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(CONFIGURATION_CHANGE, SystemClock.elapsedRealtime()); event.mPackage = "android"; - - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); - - event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE; event.mConfiguration = new Configuration(config); mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @@ -1311,14 +1314,9 @@ public class UsageStatsService extends SystemService implements return; } - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(NOTIFICATION_INTERRUPTION, SystemClock.elapsedRealtime()); event.mPackage = packageName.intern(); event.mNotificationChannelId = channelId.intern(); - - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); - - event.mEventType = Event.NOTIFICATION_INTERRUPTION; mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @@ -1329,14 +1327,9 @@ public class UsageStatsService extends SystemService implements return; } - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(SHORTCUT_INVOCATION, SystemClock.elapsedRealtime()); event.mPackage = packageName.intern(); event.mShortcutId = shortcutId.intern(); - - // This will later be converted to system time. - event.mTimeStamp = SystemClock.elapsedRealtime(); - - event.mEventType = Event.SHORTCUT_INVOCATION; mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } @@ -1372,7 +1365,7 @@ public class UsageStatsService extends SystemService implements // 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 // we are shutting down. - shutdown(); + UsageStatsService.this.shutdown(); } @Override diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index 01e566cdd85f..ec475bf26bb3 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -61,11 +61,13 @@ final class UsageStatsXmlV1 { private static final String FLAGS_ATTR = "flags"; private static final String CLASS_ATTR = "class"; private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive"; + private static final String TOTAL_TIME_VISIBLE_ATTR = "timeVisible"; private static final String TOTAL_TIME_SERVICE_USED_ATTR = "timeServiceUsed"; 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 TYPE_ATTR = "type"; + private static final String INSTANCE_ID_ATTR = "instanceId"; private static final String SHORTCUT_ID_ATTR = "shortcutId"; private static final String STANDBY_BUCKET_ATTR = "standbyBucket"; private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount"; @@ -75,6 +77,7 @@ final class UsageStatsXmlV1 { // Time attributes stored as an offset of the beginTime. private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive"; + private static final String LAST_TIME_VISIBLE_ATTR = "lastTimeVisible"; private static final String LAST_TIME_SERVICE_USED_ATTR = "lastTimeServiceUsed"; private static final String END_TIME_ATTR = "endTime"; private static final String TIME_ATTR = "time"; @@ -92,6 +95,13 @@ final class UsageStatsXmlV1 { parser, LAST_TIME_ACTIVE_ATTR); try { + stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute( + parser, LAST_TIME_VISIBLE_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse mLastTimeVisible", e); + } + + try { stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_SERVICE_USED_ATTR); } catch (IOException e) { @@ -101,8 +111,14 @@ final class UsageStatsXmlV1 { stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); try { + stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse mTotalTimeVisible", e); + } + + try { stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser, - TOTAL_TIME_SERVICE_USED_ATTR); + TOTAL_TIME_SERVICE_USED_ATTR); } catch (IOException e) { Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e); } @@ -199,6 +215,13 @@ final class UsageStatsXmlV1 { event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR); event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR); + + try { + event.mInstanceId = XmlUtils.readIntAttribute(parser, INSTANCE_ID_ATTR); + } catch (IOException e) { + Log.e(TAG, "Failed to parse mInstanceId", e); + } + switch (event.mEventType) { case UsageEvents.Event.CONFIGURATION_CHANGE: event.mConfiguration = new Configuration(); @@ -227,10 +250,13 @@ final class UsageStatsXmlV1 { // Write the time offset. XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, usageStats.mLastTimeUsed - stats.beginTime); + XmlUtils.writeLongAttribute(xml, LAST_TIME_VISIBLE_ATTR, + usageStats.mLastTimeVisible - stats.beginTime); XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR, usageStats.mLastTimeForegroundServiceUsed - stats.beginTime); XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName); XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground); + XmlUtils.writeLongAttribute(xml, TOTAL_TIME_VISIBLE_ATTR, usageStats.mTotalTimeVisible); XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR, usageStats.mTotalTimeForegroundServiceUsed); XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); @@ -317,6 +343,7 @@ final class UsageStatsXmlV1 { } XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags); XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType); + XmlUtils.writeIntAttribute(xml, INSTANCE_ID_ATTR, event.mInstanceId); switch (event.mEventType) { case UsageEvents.Event.CONFIGURATION_CHANGE: diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 94d7dbb49b74..5128ae1f9130 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -16,10 +16,18 @@ package com.android.server.usage; +import static android.app.usage.UsageStatsManager.INTERVAL_BEST; +import static android.app.usage.UsageStatsManager.INTERVAL_COUNT; +import static android.app.usage.UsageStatsManager.INTERVAL_DAILY; +import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY; +import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY; +import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY; + import android.app.usage.ConfigurationStats; import android.app.usage.EventList; import android.app.usage.EventStats; import android.app.usage.UsageEvents; +import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.Context; @@ -29,6 +37,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.SparseIntArray; import com.android.internal.util.IndentingPrintWriter; import com.android.server.usage.UsageStatsDatabase.StatCombiner; @@ -65,7 +74,7 @@ class UserUsageStatsService { private final int mUserId; // STOPSHIP: Temporary member variable for debugging b/110930764. - private UsageEvents.Event mLastEvent; + private Event mLastEvent; private static final long[] INTERVAL_LENGTH = new long[] { UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS, @@ -87,7 +96,7 @@ class UserUsageStatsService { mContext = context; mDailyExpiryDate = new UnixCalendar(0); mDatabase = new UsageStatsDatabase(usageStatsDir); - mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT]; + mCurrentStats = new IntervalStats[INTERVAL_COUNT]; mListener = listener; mLogPrefix = "User[" + Integer.toString(userId) + "] "; mUserId = userId; @@ -125,28 +134,6 @@ class UserUsageStatsService { updateRolloverDeadline(); } - // Now close off any events that were open at the time this was saved. - for (IntervalStats stat : mCurrentStats) { - final int pkgCount = stat.packageStats.size(); - for (int i = 0; i < pkgCount; i++) { - final UsageStats pkgStats = stat.packageStats.valueAt(i); - if (!pkgStats.mLastForegroundActivityEventMap.isEmpty() - || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) { - if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) { - stat.update(pkgStats.mPackageName, null, stat.lastTimeSaved, - UsageEvents.Event.END_OF_DAY); - } - if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) { - stat.update(pkgStats.mPackageName, null , stat.lastTimeSaved, - UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE); - } - notifyStatsChanged(); - } - } - - stat.updateConfigurationStats(null, stat.lastTimeSaved); - } - if (mDatabase.isNewUpdate()) { notifyNewUpdate(); } @@ -158,40 +145,46 @@ class UserUsageStatsService { loadActiveStats(newTime); } - void reportEvent(UsageEvents.Event event) { + void reportEvent(Event event) { if (DEBUG) { Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage + "[" + event.mTimeStamp + "]: " + eventToString(event.mEventType)); } - mLastEvent = new UsageEvents.Event(event); + mLastEvent = new Event(event); if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) { // Need to rollover rolloverStats(event.mTimeStamp); } - final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY]; + final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY]; final Configuration newFullConfig = event.mConfiguration; - if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE && - currentDailyStats.activeConfiguration != null) { + if (event.mEventType == Event.CONFIGURATION_CHANGE + && currentDailyStats.activeConfiguration != null) { // Make the event configuration a delta. event.mConfiguration = Configuration.generateDelta( currentDailyStats.activeConfiguration, newFullConfig); } - if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) { + if (event.mEventType != Event.SYSTEM_INTERACTION + // ACTIVITY_DESTROYED is a private event. If there is preceding ACTIVITY_STOPPED + // ACTIVITY_DESTROYED will be dropped. Otherwise it will be converted to + // ACTIVITY_STOPPED. + && event.mEventType != Event.ACTIVITY_DESTROYED + // FLUSH_TO_DISK is a private event. + && event.mEventType != Event.FLUSH_TO_DISK) { currentDailyStats.addEvent(event); } boolean incrementAppLaunch = false; - if (event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND) { + if (event.mEventType == Event.ACTIVITY_RESUMED) { if (event.mPackage != null && !event.mPackage.equals(mLastBackgroundedPackage)) { incrementAppLaunch = true; } - } else if (event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND) { + } else if (event.mEventType == Event.ACTIVITY_PAUSED) { if (event.mPackage != null) { mLastBackgroundedPackage = event.mPackage; } @@ -199,10 +192,10 @@ class UserUsageStatsService { for (IntervalStats stats : mCurrentStats) { switch (event.mEventType) { - case UsageEvents.Event.CONFIGURATION_CHANGE: { + case Event.CONFIGURATION_CHANGE: { stats.updateConfigurationStats(newFullConfig, event.mTimeStamp); } break; - case UsageEvents.Event.CHOOSER_ACTION: { + case Event.CHOOSER_ACTION: { stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction); String[] annotations = event.mContentAnnotations; if (annotations != null) { @@ -211,21 +204,21 @@ class UserUsageStatsService { } } } break; - case UsageEvents.Event.SCREEN_INTERACTIVE: { + case Event.SCREEN_INTERACTIVE: { stats.updateScreenInteractive(event.mTimeStamp); } break; - case UsageEvents.Event.SCREEN_NON_INTERACTIVE: { + case Event.SCREEN_NON_INTERACTIVE: { stats.updateScreenNonInteractive(event.mTimeStamp); } break; - case UsageEvents.Event.KEYGUARD_SHOWN: { + case Event.KEYGUARD_SHOWN: { stats.updateKeyguardShown(event.mTimeStamp); } break; - case UsageEvents.Event.KEYGUARD_HIDDEN: { + case Event.KEYGUARD_HIDDEN: { stats.updateKeyguardHidden(event.mTimeStamp); } break; default: { stats.update(event.mPackage, event.getClassName(), - event.mTimeStamp, event.mEventType); + event.mTimeStamp, event.mEventType, event.mInstanceId); if (incrementAppLaunch) { stats.incrementAppLaunchCount(event.mPackage); } @@ -286,12 +279,12 @@ class UserUsageStatsService { */ private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime, StatCombiner<T> combiner) { - if (intervalType == UsageStatsManager.INTERVAL_BEST) { + if (intervalType == INTERVAL_BEST) { intervalType = mDatabase.findBestFitBucket(beginTime, endTime); if (intervalType < 0) { // Nothing saved to disk yet, so every stat is just as equal (no rollover has // occurred. - intervalType = UsageStatsManager.INTERVAL_DAILY; + intervalType = INTERVAL_DAILY; } } @@ -316,10 +309,10 @@ class UserUsageStatsService { } // STOPSHIP: Temporary logging for b/110930764. - if (intervalType == UsageStatsManager.INTERVAL_DAILY + if (intervalType == INTERVAL_DAILY && mLastEvent != null && mLastEvent.mTimeStamp >= beginTime) { final IntervalStats diskStats = mDatabase.getLatestUsageStats( - UsageStatsManager.INTERVAL_DAILY); + INTERVAL_DAILY); StringBuilder sb = new StringBuilder(256); sb.append("Last 24 hours of UsageStats missing! timeRange : "); sb.append(beginTime); @@ -395,11 +388,11 @@ class UserUsageStatsService { UsageEvents queryEvents(final long beginTime, final long endTime, boolean obfuscateInstantApps) { final ArraySet<String> names = new ArraySet<>(); - List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY, - beginTime, endTime, new StatCombiner<UsageEvents.Event>() { + List<Event> results = queryStats(INTERVAL_DAILY, + beginTime, endTime, new StatCombiner<Event>() { @Override public void combine(IntervalStats stats, boolean mutable, - List<UsageEvents.Event> accumulatedResult) { + List<Event> accumulatedResult) { if (stats.events == null) { return; } @@ -411,11 +404,13 @@ class UserUsageStatsService { return; } - UsageEvents.Event event = stats.events.get(i); + Event event = stats.events.get(i); if (obfuscateInstantApps) { event = event.getObfuscatedIfInstantApp(); } - names.add(event.mPackage); + if (event.mPackage != null) { + names.add(event.mPackage); + } if (event.mClass != null) { names.add(event.mClass); } @@ -437,7 +432,7 @@ class UserUsageStatsService { final String packageName) { final ArraySet<String> names = new ArraySet<>(); names.add(packageName); - final List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY, + final List<Event> results = queryStats(INTERVAL_DAILY, beginTime, endTime, (stats, mutable, accumulatedResult) -> { if (stats.events == null) { return; @@ -450,7 +445,7 @@ class UserUsageStatsService { return; } - final UsageEvents.Event event = stats.events.get(i); + final Event event = stats.events.get(i); if (!packageName.equals(event.mPackage)) { continue; } @@ -492,33 +487,33 @@ class UserUsageStatsService { // Make a note of which components need a new CONTINUE_PREVIOUS_DAY or // CONTINUING_FOREGROUND_SERVICE entry. final Configuration previousConfig = - mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration; - ArraySet<String> continuePreviousDay = new ArraySet<>(); - ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundActivity = + mCurrentStats[INTERVAL_DAILY].activeConfiguration; + ArraySet<String> continuePkgs = new ArraySet<>(); + ArrayMap<String, SparseIntArray> continueActivity = new ArrayMap<>(); - ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundService = + ArrayMap<String, ArrayMap<String, Integer>> continueForegroundService = new ArrayMap<>(); for (IntervalStats stat : mCurrentStats) { final int pkgCount = stat.packageStats.size(); for (int i = 0; i < pkgCount; i++) { final UsageStats pkgStats = stat.packageStats.valueAt(i); - if (!pkgStats.mLastForegroundActivityEventMap.isEmpty() - || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) { - if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) { - continuePreviousDayForegroundActivity.put(pkgStats.mPackageName, - pkgStats.mLastForegroundActivityEventMap); + if (pkgStats.mActivities.size() > 0 + || !pkgStats.mForegroundServices.isEmpty()) { + if (pkgStats.mActivities.size() > 0) { + continueActivity.put(pkgStats.mPackageName, + pkgStats.mActivities); stat.update(pkgStats.mPackageName, null, mDailyExpiryDate.getTimeInMillis() - 1, - UsageEvents.Event.END_OF_DAY); + Event.END_OF_DAY, 0); } - if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) { - continuePreviousDayForegroundService.put(pkgStats.mPackageName, - pkgStats.mLastForegroundServiceEventMap); + if (!pkgStats.mForegroundServices.isEmpty()) { + continueForegroundService.put(pkgStats.mPackageName, + pkgStats.mForegroundServices); stat.update(pkgStats.mPackageName, null, mDailyExpiryDate.getTimeInMillis() - 1, - UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE); + Event.ROLLOVER_FOREGROUND_SERVICE, 0); } - continuePreviousDay.add(pkgStats.mPackageName); + continuePkgs.add(pkgStats.mPackageName); notifyStatsChanged(); } } @@ -532,27 +527,27 @@ class UserUsageStatsService { mDatabase.prune(currentTimeMillis); loadActiveStats(currentTimeMillis); - final int continueCount = continuePreviousDay.size(); + final int continueCount = continuePkgs.size(); for (int i = 0; i < continueCount; i++) { - String pkgName = continuePreviousDay.valueAt(i); - final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime; + String pkgName = continuePkgs.valueAt(i); + final long beginTime = mCurrentStats[INTERVAL_DAILY].beginTime; for (IntervalStats stat : mCurrentStats) { - if (continuePreviousDayForegroundActivity.containsKey(pkgName)) { - final ArrayMap<String, Integer> foregroundActivityEventMap = - continuePreviousDayForegroundActivity.get(pkgName); - final int size = foregroundActivityEventMap.size(); + if (continueActivity.containsKey(pkgName)) { + final SparseIntArray eventMap = + continueActivity.get(pkgName); + final int size = eventMap.size(); for (int j = 0; j < size; j++) { - stat.update(pkgName, foregroundActivityEventMap.keyAt(j), beginTime, - UsageEvents.Event.CONTINUE_PREVIOUS_DAY); + stat.update(pkgName, null, beginTime, + eventMap.valueAt(j), eventMap.keyAt(j)); } } - if (continuePreviousDayForegroundService.containsKey(pkgName)) { - final ArrayMap<String, Integer> foregroundServiceEventMap = - continuePreviousDayForegroundService.get(pkgName); - final int size = foregroundServiceEventMap.size(); + if (continueForegroundService.containsKey(pkgName)) { + final ArrayMap<String, Integer> eventMap = + continueForegroundService.get(pkgName); + final int size = eventMap.size(); for (int j = 0; j < size; j++) { - stat.update(pkgName, foregroundServiceEventMap.keyAt(j), beginTime, - UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE); + stat.update(pkgName, eventMap.keyAt(j), beginTime, + eventMap.valueAt(j), 0); } } stat.updateConfigurationStats(previousConfig, beginTime); @@ -611,7 +606,7 @@ class UserUsageStatsService { private void updateRolloverDeadline() { mDailyExpiryDate.setTimeInMillis( - mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime); + mCurrentStats[INTERVAL_DAILY].beginTime); mDailyExpiryDate.addDays(1); Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " + sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" + @@ -660,7 +655,7 @@ class UserUsageStatsService { } - void printEvent(IndentingPrintWriter pw, UsageEvents.Event event, boolean prettyDates) { + void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) { pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates)); pw.printPair("type", eventToString(event.mEventType)); pw.printPair("package", event.mPackage); @@ -673,10 +668,15 @@ class UserUsageStatsService { if (event.mShortcutId != null) { pw.printPair("shortcutId", event.mShortcutId); } - if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) { + if (event.mEventType == Event.STANDBY_BUCKET_CHANGED) { pw.printPair("standbyBucket", event.getStandbyBucket()); pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason())); + } else if (event.mEventType == Event.ACTIVITY_RESUMED + || event.mEventType == Event.ACTIVITY_PAUSED + || event.mEventType == Event.ACTIVITY_STOPPED) { + pw.printPair("instanceId", event.getInstanceId()); } + if (event.mNotificationChannelId != null) { pw.printPair("channelId", event.mNotificationChannelId); } @@ -691,11 +691,11 @@ class UserUsageStatsService { final long beginTime = yesterday.getTimeInMillis(); - List<UsageEvents.Event> events = queryStats(UsageStatsManager.INTERVAL_DAILY, - beginTime, endTime, new StatCombiner<UsageEvents.Event>() { + List<Event> events = queryStats(INTERVAL_DAILY, + beginTime, endTime, new StatCombiner<Event>() { @Override public void combine(IntervalStats stats, boolean mutable, - List<UsageEvents.Event> accumulatedResult) { + List<Event> accumulatedResult) { if (stats.events == null) { return; } @@ -707,7 +707,7 @@ class UserUsageStatsService { return; } - UsageEvents.Event event = stats.events.get(i); + Event event = stats.events.get(i); if (pkg != null && !pkg.equals(event.mPackage)) { continue; } @@ -727,7 +727,7 @@ class UserUsageStatsService { pw.println(")"); if (events != null) { pw.increaseIndent(); - for (UsageEvents.Event event : events) { + for (Event event : events) { printEvent(pw, event, prettyDates); } pw.decreaseIndent(); @@ -772,9 +772,17 @@ class UserUsageStatsService { continue; } pw.printPair("package", usageStats.mPackageName); - pw.printPair("totalTime", + pw.printPair("totalTimeUsed", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates)); - pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates)); + pw.printPair("lastTimeUsed", formatDateTime(usageStats.mLastTimeUsed, prettyDates)); + pw.printPair("totalTimeVisible", + formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates)); + pw.printPair("lastTimeVisible", + formatDateTime(usageStats.mLastTimeVisible, prettyDates)); + pw.printPair("totalTimeFS", + formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates)); + pw.printPair("lastTimeFS", + formatDateTime(usageStats.mLastTimeForegroundServiceUsed, prettyDates)); pw.printPair("appLaunchCount", usageStats.mAppLaunchCount); pw.println(); } @@ -845,7 +853,7 @@ class UserUsageStatsService { final EventList events = stats.events; final int eventCount = events != null ? events.size() : 0; for (int i = 0; i < eventCount; i++) { - final UsageEvents.Event event = events.get(i); + final Event event = events.get(i); if (pkg != null && !pkg.equals(event.mPackage)) { continue; } @@ -858,13 +866,13 @@ class UserUsageStatsService { private static String intervalToString(int interval) { switch (interval) { - case UsageStatsManager.INTERVAL_DAILY: + case INTERVAL_DAILY: return "daily"; - case UsageStatsManager.INTERVAL_WEEKLY: + case INTERVAL_WEEKLY: return "weekly"; - case UsageStatsManager.INTERVAL_MONTHLY: + case INTERVAL_MONTHLY: return "monthly"; - case UsageStatsManager.INTERVAL_YEARLY: + case INTERVAL_YEARLY: return "yearly"; default: return "?"; @@ -873,47 +881,49 @@ class UserUsageStatsService { private static String eventToString(int eventType) { switch (eventType) { - case UsageEvents.Event.NONE: + case Event.NONE: return "NONE"; - case UsageEvents.Event.MOVE_TO_BACKGROUND: - return "MOVE_TO_BACKGROUND"; - case UsageEvents.Event.MOVE_TO_FOREGROUND: - return "MOVE_TO_FOREGROUND"; - case UsageEvents.Event.FOREGROUND_SERVICE_START: + case Event.ACTIVITY_PAUSED: + return "ACTIVITY_PAUSED"; + case Event.ACTIVITY_RESUMED: + return "ACTIVITY_RESUMED"; + case Event.FOREGROUND_SERVICE_START: return "FOREGROUND_SERVICE_START"; - case UsageEvents.Event.FOREGROUND_SERVICE_STOP: + case Event.FOREGROUND_SERVICE_STOP: return "FOREGROUND_SERVICE_STOP"; - case UsageEvents.Event.END_OF_DAY: + case Event.ACTIVITY_STOPPED: + return "ACTIVITY_STOPPED"; + case Event.END_OF_DAY: return "END_OF_DAY"; - case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE: + case Event.ROLLOVER_FOREGROUND_SERVICE: return "ROLLOVER_FOREGROUND_SERVICE"; - case UsageEvents.Event.CONTINUE_PREVIOUS_DAY: + case Event.CONTINUE_PREVIOUS_DAY: return "CONTINUE_PREVIOUS_DAY"; - case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE: + case Event.CONTINUING_FOREGROUND_SERVICE: return "CONTINUING_FOREGROUND_SERVICE"; - case UsageEvents.Event.CONFIGURATION_CHANGE: + case Event.CONFIGURATION_CHANGE: return "CONFIGURATION_CHANGE"; - case UsageEvents.Event.SYSTEM_INTERACTION: + case Event.SYSTEM_INTERACTION: return "SYSTEM_INTERACTION"; - case UsageEvents.Event.USER_INTERACTION: + case Event.USER_INTERACTION: return "USER_INTERACTION"; - case UsageEvents.Event.SHORTCUT_INVOCATION: + case Event.SHORTCUT_INVOCATION: return "SHORTCUT_INVOCATION"; - case UsageEvents.Event.CHOOSER_ACTION: + case Event.CHOOSER_ACTION: return "CHOOSER_ACTION"; - case UsageEvents.Event.NOTIFICATION_SEEN: + case Event.NOTIFICATION_SEEN: return "NOTIFICATION_SEEN"; - case UsageEvents.Event.STANDBY_BUCKET_CHANGED: + case Event.STANDBY_BUCKET_CHANGED: return "STANDBY_BUCKET_CHANGED"; - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + case Event.NOTIFICATION_INTERRUPTION: return "NOTIFICATION_INTERRUPTION"; - case UsageEvents.Event.SLICE_PINNED: + case Event.SLICE_PINNED: return "SLICE_PINNED"; - case UsageEvents.Event.SLICE_PINNED_PRIV: + case Event.SLICE_PINNED_PRIV: return "SLICE_PINNED_PRIV"; - case UsageEvents.Event.SCREEN_INTERACTIVE: + case Event.SCREEN_INTERACTIVE: return "SCREEN_INTERACTIVE"; - case UsageEvents.Event.SCREEN_NON_INTERACTIVE: + case Event.SCREEN_NON_INTERACTIVE: return "SCREEN_NON_INTERACTIVE"; case UsageEvents.Event.KEYGUARD_SHOWN: return "KEYGUARD_SHOWN"; diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java index be74a6d162ae..7a5e7325ad22 100644 --- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java +++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java @@ -92,11 +92,11 @@ public class UsageStatsDatabasePerfTest { event.mPackage = "fake.package.name" + pkg; event.mClass = event.mPackage + ".class1"; event.mTimeStamp = 1; - event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND; + event.mEventType = UsageEvents.Event.ACTIVITY_RESUMED; for (int evt = 0; evt < eventsPerPackage; evt++) { intervalStats.events.insert(event); intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, - event.mEventType); + event.mEventType, 1); } } } diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java index 3480e96b3547..53afa26796ea 100644 --- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java +++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java @@ -21,13 +21,14 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.os.Bundle; import android.os.Handler; -import androidx.collection.CircularArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; +import androidx.collection.CircularArray; + public class UsageLogActivity extends ListActivity implements Runnable { private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; @@ -155,10 +156,10 @@ public class UsageLogActivity extends ListActivity implements Runnable { private String eventToString(int eventType) { switch (eventType) { - case UsageEvents.Event.MOVE_TO_FOREGROUND: + case UsageEvents.Event.ACTIVITY_RESUMED: return "Foreground"; - case UsageEvents.Event.MOVE_TO_BACKGROUND: + case UsageEvents.Event.ACTIVITY_PAUSED: return "Background"; case UsageEvents.Event.CONFIGURATION_CHANGE: |