diff options
author | 2018-10-04 15:05:21 -0700 | |
---|---|---|
committer | 2018-11-12 17:41:03 -0800 | |
commit | e361a23bba60ae77fc5e09666c44f9159eb8a0d0 (patch) | |
tree | 566ab038052384728df63952af3a2190f80f451b | |
parent | 581cccdde99adfae7d05b51fa86acdb83d80b1bf (diff) |
Add UsageStats events for foreground service start/stop.
1. Send FOREGROUND_SERVICE_START event when foreground service starts.
Send FOREGROUND_SERVICE_STOP event when foreground service stops.
2. One app can multiple foreground services and multiple services can be
started. Because this, in UsageStats, change mLastForegroundEvent to
className to event map, do this for both activity and foreground
service. Change UsageStatsProto and UsageStatsXmlV1 to support this
change.
3. Add more test cases in UsageStatsTest.java.
Test: start music player which is foreground service, observce these
two events when start play and pause play.
Change-Id: I3dc14f5b73cc114a53b8c51f90d3011d9ace35ac
Bug: 112002260
Test: atest UsageStatsTest#testForegroundService
atest frameworks/base/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
atest frameworks/base/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
15 files changed, 971 insertions, 140 deletions
diff --git a/api/current.txt b/api/current.txt index 533c70f9221c..5c3562cfd72b 100755 --- a/api/current.txt +++ b/api/current.txt @@ -7580,6 +7580,8 @@ package android.app.usage { method public java.lang.String getShortcutId(); method public long getTimeStamp(); 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 @@ -7597,9 +7599,11 @@ package android.app.usage { method public void add(android.app.usage.UsageStats); method public int describeContents(); method public long getFirstTimeStamp(); + method public long getLastTimeForegroundServiceUsed(); method public long getLastTimeStamp(); method public long getLastTimeUsed(); method public java.lang.String getPackageName(); + method public long getTotalTimeForegroundServiceUsed(); method public long getTotalTimeInForeground(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.usage.UsageStats> CREATOR; diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 308b39efc3b1..3a5975aea628 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -50,12 +50,20 @@ public final class UsageEvents implements Parcelable { public static final int NONE = 0; /** - * An event type denoting that a component moved to the foreground. + * 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. */ public static final int MOVE_TO_FOREGROUND = 1; /** - * An event type denoting that a component moved to the background. + * An event type denoting that an {@link android.app.Activity} moved to the background. + * 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 background. */ public static final int MOVE_TO_BACKGROUND = 2; @@ -166,10 +174,43 @@ public final class UsageEvents implements Parcelable { public static final int KEYGUARD_HIDDEN = 18; /** + * An event type denoting start of a foreground service. + * 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 foreground services, this event is reported for each service + * that is started. + */ + public static final int FOREGROUND_SERVICE_START = 19; + + /** + * An event type denoting stop of a foreground service. + * 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 foreground services, this event is reported for each service + * that is stopped. + */ + public static final int FOREGROUND_SERVICE_STOP = 20; + + /** + * An event type denoting that a foreground service is at started state at beginning of a + * time interval. + * This is effectively treated as a {@link #FOREGROUND_SERVICE_START}. + * {@hide} + */ + public static final int CONTINUING_FOREGROUND_SERVICE = 21; + + /** + * An event type denoting that a foreground service is at started state when the stats + * rolled-over at the end of a time interval. + * {@hide} + */ + public static final int ROLLOVER_FOREGROUND_SERVICE = 22; + + /** * Keep in sync with the greatest event type value. * @hide */ - public static final int MAX_EVENT_TYPE = 18; + public static final int MAX_EVENT_TYPE = 22; /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 0659a237d522..73426e495037 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -16,6 +16,15 @@ package android.app.usage; +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.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; import android.annotation.UnsupportedAppUsage; import android.os.Bundle; @@ -48,19 +57,32 @@ public final class UsageStats implements Parcelable { public long mEndTimeStamp; /** - * Last time used by the user with an explicit action (notification, activity launch). + * Last time used by the user with an explicit action (notification, activity launch) * {@hide} */ @UnsupportedAppUsage public long mLastTimeUsed; /** + * Total time this package's activity is in foreground. * {@hide} */ @UnsupportedAppUsage public long mTotalTimeInForeground; /** + * Last time foreground service is started. + * {@hide} + */ + public long mLastTimeForegroundServiceUsed; + + /** + * Total time this package's foreground service is started. + * {@hide} + */ + public long mTotalTimeForegroundServiceUsed; + + /** * {@hide} */ @UnsupportedAppUsage @@ -71,16 +93,36 @@ public final class UsageStats implements Parcelable { */ public int mAppLaunchCount; - /** + /** Last activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event. * {@hide} + * @deprecated use {@link #mLastForegroundActivityEventMap} 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. + * {@hide} + */ + public ArrayMap<String, Integer> mLastForegroundActivityEventMap = new ArrayMap<>(); + + /** + * 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. + * Key is foreground service class name. + * Value is last foreground service FOREGROUND_SERVICE_START ot FOREGROUND_SERVICE_STOP event. * {@hide} */ - public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts; + public ArrayMap<String, Integer> mLastForegroundServiceEventMap = new ArrayMap<>(); + + /** + * {@hide} + */ + public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts = new ArrayMap<>(); /** * {@hide} @@ -93,10 +135,14 @@ public final class UsageStats implements Parcelable { mBeginTimeStamp = stats.mBeginTimeStamp; mEndTimeStamp = stats.mEndTimeStamp; mLastTimeUsed = stats.mLastTimeUsed; + mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed; mTotalTimeInForeground = stats.mTotalTimeInForeground; + mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed; mLaunchCount = stats.mLaunchCount; mAppLaunchCount = stats.mAppLaunchCount; mLastEvent = stats.mLastEvent; + mLastForegroundActivityEventMap = stats.mLastForegroundActivityEventMap; + mLastForegroundServiceEventMap = stats.mLastForegroundServiceEventMap; mChooserCounts = stats.mChooserCounts; } @@ -136,7 +182,7 @@ public final class UsageStats implements Parcelable { } /** - * Get the last time this package was used, measured in milliseconds since the epoch. + * Get the last time this package's activity was used, measured in milliseconds since the epoch. * <p/> * See {@link System#currentTimeMillis()}. */ @@ -152,6 +198,23 @@ public final class UsageStats implements Parcelable { } /** + * Get the last time this package's foreground service was used, measured in milliseconds since + * the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. + */ + public long getLastTimeForegroundServiceUsed() { + return mLastTimeForegroundServiceUsed; + } + + /** + * Get the total time this package's foreground services are started, measured in milliseconds. + */ + public long getTotalTimeForegroundServiceUsed() { + return mTotalTimeForegroundServiceUsed; + } + + /** * Returns the number of times the app was launched as an activity from outside of the app. * Excludes intra-app activity transitions. * @hide @@ -161,6 +224,19 @@ public final class UsageStats implements Parcelable { return mAppLaunchCount; } + private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) { + final int size = right.size(); + for (int i = 0; i < size; i++) { + final String className = right.keyAt(i); + final Integer event = right.valueAt(i); + if (left.containsKey(className)) { + left.put(className, Math.max(left.get(className), event)); + } else { + left.put(className, event); + } + } + } + /** * Add the statistics from the right {@link UsageStats} to the left. The package name for * both {@link UsageStats} objects must be the same. @@ -179,12 +255,16 @@ 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. - mLastEvent = Math.max(mLastEvent, right.mLastEvent); + mergeEventMap(mLastForegroundActivityEventMap, right.mLastForegroundActivityEventMap); + mergeEventMap(mLastForegroundServiceEventMap, right.mLastForegroundServiceEventMap); mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed); + mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed, + right.mLastTimeForegroundServiceUsed); } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; + mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed; mLaunchCount += right.mLaunchCount; mAppLaunchCount += right.mAppLaunchCount; if (mChooserCounts == null) { @@ -209,6 +289,161 @@ 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 + */ + private boolean isActivityInForeground(int event) { + return event == MOVE_TO_FOREGROUND + || event == CONTINUE_PREVIOUS_DAY; + } + + /** + * 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 + */ + private boolean isForegroundServiceStarted(int event) { + return event == FOREGROUND_SERVICE_START + || event == CONTINUING_FOREGROUND_SERVICE; + } + + /** + * 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 + */ + private boolean isAppInUse() { + return !mLastForegroundActivityEventMap.isEmpty() + || !mLastForegroundServiceEventMap.isEmpty(); + } + + /** + * Update by an event of an activity. + * @param className className of the activity. + * @param timeStamp timeStamp of the event. + * @param eventType type of the event. + * @hide + */ + private void updateForegroundActivity(String className, long timeStamp, int eventType) { + if (eventType != MOVE_TO_BACKGROUND + && eventType != MOVE_TO_FOREGROUND + && eventType != END_OF_DAY) { + return; + } + + final Integer lastEvent = mLastForegroundActivityEventMap.get(className); + if (lastEvent != null) { + if (isActivityInForeground(lastEvent)) { + if (timeStamp > mLastTimeUsed) { + mTotalTimeInForeground += timeStamp - mLastTimeUsed; + 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); + } + } + + /** + * Update by an event of an foreground service. + * @param className className of the foreground service. + * @param timeStamp timeStamp of the event. + * @param eventType type of the event. + * @hide + */ + private void updateForegroundService(String className, long timeStamp, int eventType) { + if (eventType != FOREGROUND_SERVICE_STOP + && eventType != FOREGROUND_SERVICE_START + && eventType != ROLLOVER_FOREGROUND_SERVICE) { + return; + } + final Integer lastEvent = mLastForegroundServiceEventMap.get(className); + if (lastEvent != null) { + if (isForegroundServiceStarted(lastEvent)) { + if (timeStamp > mLastTimeForegroundServiceUsed) { + mTotalTimeForegroundServiceUsed += + timeStamp - mLastTimeForegroundServiceUsed; + 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); + } + } + + /** + * 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 timeStamp Epoch timestamp in milliseconds. + * @param eventType event type as in {@link UsageEvents.Event} + * @hide + */ + public void update(String className, long timeStamp, int eventType) { + switch(eventType) { + case MOVE_TO_BACKGROUND: + case MOVE_TO_FOREGROUND: + updateForegroundActivity(className, timeStamp, eventType); + 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); + } + break; + case CONTINUE_PREVIOUS_DAY: + mLastTimeUsed = timeStamp; + mLastForegroundActivityEventMap.put(className, eventType); + break; + case FOREGROUND_SERVICE_STOP: + case FOREGROUND_SERVICE_START: + 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); + } + break; + case CONTINUING_FOREGROUND_SERVICE: + mLastTimeForegroundServiceUsed = timeStamp; + mLastForegroundServiceEventMap.put(className, eventType); + break; + default: + break; + } + mEndTimeStamp = timeStamp; + + if (eventType == MOVE_TO_FOREGROUND) { + mLaunchCount += 1; + } + } + @Override public int describeContents() { return 0; @@ -220,7 +455,9 @@ public final class UsageStats implements Parcelable { dest.writeLong(mBeginTimeStamp); dest.writeLong(mEndTimeStamp); dest.writeLong(mLastTimeUsed); + dest.writeLong(mLastTimeForegroundServiceUsed); dest.writeLong(mTotalTimeInForeground); + dest.writeLong(mTotalTimeForegroundServiceUsed); dest.writeInt(mLaunchCount); dest.writeInt(mAppLaunchCount); dest.writeInt(mLastEvent); @@ -239,6 +476,22 @@ 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)); + } + 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)); + } + dest.writeBundle(foregroundServiceEventBundle); } public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() { @@ -249,7 +502,9 @@ public final class UsageStats implements Parcelable { stats.mBeginTimeStamp = in.readLong(); stats.mEndTimeStamp = in.readLong(); stats.mLastTimeUsed = in.readLong(); + stats.mLastTimeForegroundServiceUsed = in.readLong(); stats.mTotalTimeInForeground = in.readLong(); + stats.mTotalTimeForegroundServiceUsed = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mAppLaunchCount = in.readInt(); stats.mLastEvent = in.readInt(); @@ -272,9 +527,20 @@ public final class UsageStats implements Parcelable { } } } + readBundleToEventMap(stats.mLastForegroundActivityEventMap, in.readBundle()); + readBundleToEventMap(stats.mLastForegroundServiceEventMap, in.readBundle()); return stats; } + private void readBundleToEventMap(ArrayMap<String, Integer> eventMap, Bundle bundle) { + if (bundle != null) { + for (String className : bundle.keySet()) { + final int event = bundle.getInt(className); + eventMap.put(className, event); + } + } + } + @Override public UsageStats[] newArray(int size) { return new UsageStats[size]; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 6d7400e0ef73..55148511ed10 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -192,7 +192,10 @@ public final class UsageStatsManager { public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE = 0x000C; /** @hide */ public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D; - + /** @hide */ + public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E; + /** @hide */ + public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F; /** @hide */ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001; diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto index 941c81fbb8df..3d60a86d86c9 100644 --- a/core/proto/android/server/usagestatsservice.proto +++ b/core/proto/android/server/usagestatsservice.proto @@ -45,11 +45,15 @@ message IntervalStatsProto { optional string package = 1; // package_index contains the index + 1 of the package name in the string pool optional int32 package_index = 2; + // Time attributes stored as an offset of the IntervalStats's beginTime. optional int64 last_time_active_ms = 3; optional int64 total_time_active_ms = 4; optional int32 last_event = 5; optional int32 app_launch_count = 6; repeated ChooserAction chooser_actions = 7; + // 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; } // Stores the relevant information an IntervalStats will have about a Configuration @@ -86,6 +90,8 @@ message IntervalStatsProto { // stringpool contains all the package and class names used by UsageStats and Event // They will hold a number that is equal to the index + 1 of their string in the pool optional StringPool stringpool = 2; + optional int32 major_version = 3; + optional int32 minor_version = 4; // The following fields contain aggregated usage stats data optional CountAndTime interactive = 10; diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java index c6d077dee18f..1f047f9e6d10 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java @@ -16,8 +16,19 @@ package android.app.usage; -import static com.google.common.truth.Truth.assertThat; +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.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 android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -46,7 +57,7 @@ public class UsageStatsTest { left.add(right); - assertThat(left.getFirstTimeStamp()).isEqualTo(99999); + assertEquals(left.getFirstTimeStamp(), 99999); } @Test @@ -58,7 +69,7 @@ public class UsageStatsTest { left.add(right); - assertThat(left.getLastTimeStamp()).isEqualTo(100001); + assertEquals(left.getLastTimeStamp(), 100001); } @Test @@ -72,7 +83,7 @@ public class UsageStatsTest { left.add(right); - assertThat(left.getLastTimeUsed()).isEqualTo(200001); + assertEquals(left.getLastTimeUsed(), 200001); } @Test @@ -86,7 +97,7 @@ public class UsageStatsTest { left.add(right); - assertThat(left.getLastTimeUsed()).isEqualTo(200000); + assertEquals(left.getLastTimeUsed(), 200000); } @Test @@ -100,6 +111,373 @@ public class UsageStatsTest { left.add(right); - assertThat(left.getTotalTimeInForeground()).isEqualTo(11); + assertEquals(left.getTotalTimeInForeground(), 11); + } + + @Test + public void testParcelable() { + left.mPackageName = "com.test"; + 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); + + Parcel p = Parcel.obtain(); + left.writeToParcel(p, 0); + p.setDataPosition(0); + right = UsageStats.CREATOR.createFromParcel(p); + compareUsageStats(left, right); + } + + @Test + public void testForegroundActivity() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND); + assertEquals(left.mLastTimeUsed, 200000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), + new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mLaunchCount, 1); + + left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 350000); + assertFalse(left.mLastForegroundActivityEventMap.containsKey("com.test.activity1")); + assertEquals(left.mTotalTimeInForeground, 350000 - 200000); + } + + @Test + public void testEvent_CONTINUE_PREVIOUS_DAY() { + 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", 350000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 350000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mTotalTimeInForeground, 350000 - 100000); + } + + @Test + public void testEvent_END_OF_DAY() { + 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(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); + } + + @Test + public void testForegroundActivityEventSequence() { + 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", 350000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 350000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/); + + 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*/); + } + + @Test + public void testForegroundActivityEventOutOfSequence() { + 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", 150000, MOVE_TO_FOREGROUND); + assertEquals(left.mLastTimeUsed, 150000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), + new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mLaunchCount, 1); + assertEquals(left.mTotalTimeInForeground, 50000 /*150000 - 100000*/); + + left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND); + assertEquals(left.mLastTimeUsed, 200000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), + new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mLaunchCount, 2); + assertEquals(left.mTotalTimeInForeground, 100000); + + left.update("com.test.activity1", 250000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 250000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mTotalTimeInForeground, 150000); + + left.update("com.test.activity1", 300000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 250000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null); + assertEquals(left.mTotalTimeInForeground, 150000); + + left.update("com.test.activity1", 350000, MOVE_TO_FOREGROUND); + assertEquals(left.mLastTimeUsed, 350000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), + new Integer(MOVE_TO_FOREGROUND)); + assertEquals(left.mTotalTimeInForeground, 150000); + + left.update("com.test.activity1", 400000, END_OF_DAY); + assertEquals(left.mLastTimeUsed, 400000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), + new Integer(END_OF_DAY)); + assertEquals(left.mTotalTimeInForeground, 200000); + } + + @Test + public void testTwoActivityEventSequence() { + 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); + 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); + + 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*/); + + left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null); + assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/); + + left.update(null, 500000, END_OF_DAY); + assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mTotalTimeInForeground, 350000); + } + + @Test + public void testForegroundService() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START); + assertEquals(left.mLastTimeForegroundServiceUsed, 200000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(FOREGROUND_SERVICE_START)); + assertEquals(left.mLaunchCount, 0); + + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 350000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 200000); + } + + @Test + public void testEvent_CONTINUING_FOREGROUND_SERVICE() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.service1", 100000, CONTINUING_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 100000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + assertEquals(left.mLaunchCount, 0); + + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 350000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000); + } + + @Test + public void testEvent_ROLLOVER_FOREGROUND_SERVICE() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.service1", 100000, + CONTINUING_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 100000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + assertEquals(left.mLaunchCount, 0); + + left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 350000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(ROLLOVER_FOREGROUND_SERVICE)); + assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000); + } + + @Test + public void testForegroundServiceEventSequence() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.service1", 100000, + CONTINUING_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 100000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + assertEquals(left.mLaunchCount, 0); + + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 350000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/); + + left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START); + assertEquals(left.mLastTimeForegroundServiceUsed, 450000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(FOREGROUND_SERVICE_START)); + assertEquals(left.mTotalTimeForegroundServiceUsed, 250000); + + left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 500000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 50000 /*500000 - 450000*/); + } + + @Test + public void testTwoServiceEventSequence() { + left.mPackageName = "com.test"; + left.mBeginTimeStamp = 100000; + + left.update("com.test.service1", 100000, + CONTINUING_FOREGROUND_SERVICE); + left.update("com.test.service2", 100000, + CONTINUING_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 100000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + assertEquals(left.mLaunchCount, 0); + + left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 350000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/); + + left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 450000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 100000 /*450000 - 350000*/); + + left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 450000); + assertEquals(left.mTotalTimeForegroundServiceUsed, 350000); + } + + @Test + public void testTwoActivityAndTwoServiceEventSequence() { + 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.service1", 100000, + CONTINUING_FOREGROUND_SERVICE); + left.update("com.test.service2", 100000, + CONTINUING_FOREGROUND_SERVICE); + 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"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), + new Integer(CONTINUING_FOREGROUND_SERVICE)); + 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*/); + + left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 400000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 /*400000 - 100000*/); + + left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND); + assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null); + assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/); + + left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP); + assertEquals(left.mLastTimeForegroundServiceUsed, 500000); + assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null); + assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 + 100000 /*500000 - 400000*/); + + + left.update(null, 550000, END_OF_DAY); + assertEquals(left.mLastTimeUsed, 450000); + assertEquals(left.mTotalTimeInForeground, 350000); + left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE); + assertEquals(left.mLastTimeForegroundServiceUsed, 500000); + assertEquals(left.mTotalTimeForegroundServiceUsed, 400000); + } + + void compareUsageStats(UsageStats us1, UsageStats us2) { + assertEquals(us1.mPackageName, us2.mPackageName); + assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp); + assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); + 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.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.mChooserCounts, us2.mChooserCounts); } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2d3912bda9cc..a33ac700ba95 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -17,40 +17,69 @@ package com.android.server.am; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static com.android.server.am.ActivityManagerDebugConfig.*; -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE_EXECUTING; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_EXECUTING; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityThread; +import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.IApplicationThread; +import android.app.IServiceConnection; +import android.app.Notification; import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; import android.app.ServiceStartArgs; +import android.content.ComponentName; import android.content.ComponentName.WithComponentName; +import android.content.Context; import android.content.IIntentSender; +import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; +import android.os.Message; +import android.os.Process; import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.TransactionTooLargeException; +import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; +import android.util.PrintWriterPrinter; +import android.util.Slog; +import android.util.SparseArray; +import android.util.StatsLog; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; +import android.webkit.WebViewZygote; import com.android.internal.R; import com.android.internal.app.procstats.ServiceState; @@ -64,39 +93,20 @@ import com.android.server.AppStateTracker; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.am.ActivityManagerService.ItemMatcher; - -import android.app.ActivityManager; -import android.app.AppGlobals; -import android.app.IApplicationThread; -import android.app.IServiceConnection; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.os.Binder; -import android.os.IBinder; -import android.os.Message; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.EventLog; -import android.util.PrintWriterPrinter; -import android.util.Slog; -import android.util.StatsLog; -import android.util.SparseArray; -import android.util.TimeUtils; -import android.util.proto.ProtoOutputStream; -import android.webkit.WebViewZygote; import com.android.server.uri.NeededUriGrants; import com.android.server.wm.ActivityServiceConnectionsHolder; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + public final class ActiveServices { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM; private static final String TAG_MU = TAG + POSTFIX_MU; @@ -1285,6 +1295,7 @@ public final class ActiveServices { StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName, StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER); + mAm.updateForegroundServiceUsageStats(r.name, r.userId, true); } r.postNotification(); if (r.app != null) { @@ -1334,6 +1345,7 @@ public final class ActiveServices { StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName, StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT); + mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); if (r.app != null) { mAm.updateLruProcessLocked(r.app, false, null); updateServiceForegroundLocked(r.app, true); @@ -2797,6 +2809,7 @@ public final class ActiveServices { AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName, StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT); + mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); } r.isForeground = false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e90d42fa2dce..6c4f6297fc01 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2690,8 +2690,10 @@ public class ActivityManagerService extends IActivityManager.Stub } void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) { - if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, - "updateUsageStats: comp=" + activity + "res=" + resumed); + if (DEBUG_SWITCH) { + Slog.d(TAG_SWITCH, + "updateUsageStats: comp=" + activity + "res=" + resumed); + } final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED, uid, activity.getPackageName(), @@ -2718,6 +2720,20 @@ public class ActivityManagerService extends IActivityManager.Stub } } + void updateForegroundServiceUsageStats(ComponentName service, int userId, boolean started) { + if (DEBUG_SWITCH) { + Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp=" + + service + "started=" + started); + } + synchronized (this) { + if (mUsageStatsService != null) { + mUsageStatsService.reportEvent(service, userId, + started ? UsageEvents.Event.FOREGROUND_SERVICE_START + : UsageEvents.Event.FOREGROUND_SERVICE_STOP); + } + } + } + CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) { return mAtmInternal.compatibilityInfoForPackage(ai); } 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 473682d4d906..bf7f53dd7d8f 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -16,13 +16,12 @@ package com.android.server.usage; -import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.fail; import static org.testng.Assert.assertEquals; import android.app.usage.EventList; -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; @@ -112,6 +111,8 @@ public class UsageStatsDatabaseTest { long time = System.currentTimeMillis() - (numberOfEvents*timeProgression); mIntervalStats = new IntervalStats(); + mIntervalStats.majorVersion = 7; + mIntervalStats.minorVersion = 8; mIntervalStats.beginTime = time; mIntervalStats.interactiveTracker.count = 2; mIntervalStats.interactiveTracker.duration = 111111; @@ -127,41 +128,39 @@ public class UsageStatsDatabaseTest { } for (int i = 0; i < numberOfEvents; i++) { - UsageEvents.Event event = new UsageEvents.Event(); + Event event = new Event(); final int packageInt = ((i / 3) % 7); event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps" if (packageInt == 3) { // Third app is an instant app - event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP; - } else if (packageInt == 2 || packageInt == 4) { - event.mClass = ".fake.class.name" + i % 11; + event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP; } - + event.mClass = ".fake.class.name" + i % 11; event.mTimeStamp = time; event.mEventType = i % 19; //"random" event type switch (event.mEventType) { - case UsageEvents.Event.CONFIGURATION_CHANGE: + case Event.CONFIGURATION_CHANGE: //empty config, event.mConfiguration = new Configuration(); break; - case UsageEvents.Event.SHORTCUT_INVOCATION: + case Event.SHORTCUT_INVOCATION: //"random" shortcut event.mShortcutId = "shortcut" + (i % 8); break; - case UsageEvents.Event.STANDBY_BUCKET_CHANGED: + case Event.STANDBY_BUCKET_CHANGED: //"random" bucket and reason event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8; break; - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + case Event.NOTIFICATION_INTERRUPTION: //"random" channel event.mNotificationChannelId = "channel" + (i % 5); break; } mIntervalStats.events.insert(event); - mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType); + mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType); time += timeProgression; // Arbitrary progression of time } @@ -221,29 +220,30 @@ public class UsageStatsDatabaseTest { // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground); + assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed); + assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed); // mLaunchCount not persisted, so skipped assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount); - assertEquals(us1.mLastEvent, us2.mLastEvent); assertEquals(us1.mChooserCounts, us2.mChooserCounts); } - void compareUsageEvent(UsageEvents.Event e1, UsageEvents.Event e2, int debugId) { + void compareUsageEvent(Event e1, Event e2, int debugId) { assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId); assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId); assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId); assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId); switch (e1.mEventType) { - case UsageEvents.Event.CONFIGURATION_CHANGE: + case Event.CONFIGURATION_CHANGE: assertEquals(e1.mConfiguration, e2.mConfiguration, "Usage event " + debugId + e2.mConfiguration.toString()); break; - case UsageEvents.Event.SHORTCUT_INVOCATION: + case Event.SHORTCUT_INVOCATION: assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId); break; - case UsageEvents.Event.STANDBY_BUCKET_CHANGED: + case Event.STANDBY_BUCKET_CHANGED: assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId); break; - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + case Event.NOTIFICATION_INTERRUPTION: assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId, "Usage event " + debugId); break; @@ -252,6 +252,8 @@ public class UsageStatsDatabaseTest { } void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) { + assertEquals(stats1.majorVersion, stats2.majorVersion); + assertEquals(stats1.minorVersion, stats2.minorVersion); assertEquals(stats1.beginTime, stats2.beginTime); assertEquals(stats1.endTime, stats2.endTime); assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count); diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 4f573a475ae7..152831f50770 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -27,6 +27,8 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOU import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN; @@ -844,6 +846,8 @@ public class AppStandbyController { // Inform listeners if necessary if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND + || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START + || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION || event.mEventType == UsageEvents.Event.USER_INTERACTION || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN @@ -896,6 +900,10 @@ public class AppStandbyController { 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.FOREGROUND_SERVICE_START: + return REASON_SUB_USAGE_FOREGROUND_SERVICE_START; + case UsageEvents.Event.FOREGROUND_SERVICE_STOP: + return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP; 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 db9972f96b21..84052672f6d3 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -18,7 +18,6 @@ package com.android.server.usage; import android.app.usage.ConfigurationStats; import android.app.usage.EventList; import android.app.usage.EventStats; -import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; @@ -26,12 +25,16 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.proto.ProtoInputStream; +import com.android.internal.annotations.VisibleForTesting; + import java.io.IOException; import java.util.List; -import com.android.internal.annotations.VisibleForTesting; - public class IntervalStats { + public static final int CURRENT_MAJOR_VERSION = 1; + public static final int CURRENT_MINOR_VERSION = 1; + public int majorVersion = CURRENT_MAJOR_VERSION; + public int minorVersion = CURRENT_MINOR_VERSION; public long beginTime; public long endTime; public long lastTimeSaved; @@ -219,8 +222,12 @@ public class IntervalStats { 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: return true; } return false; @@ -239,32 +246,9 @@ public class IntervalStats { * @hide */ @VisibleForTesting - public void update(String packageName, long timeStamp, int eventType) { + public void update(String packageName, String className, long timeStamp, int eventType) { UsageStats usageStats = getOrCreateUsageStats(packageName); - - // TODO(adamlesinski): Ensure that we recover from incorrect event sequences - // like double MOVE_TO_BACKGROUND, etc. - if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND || - eventType == UsageEvents.Event.END_OF_DAY) { - if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND || - usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) { - usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed; - } - } - - if (isStatefulEvent(eventType)) { - usageStats.mLastEvent = eventType; - } - - if (isUserVisibleEvent(eventType)) { - usageStats.mLastTimeUsed = timeStamp; - } - usageStats.mEndTimeStamp = timeStamp; - - if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) { - usageStats.mLaunchCount += 1; - } - + usageStats.update(className, timeStamp, eventType); endTime = timeStamp; } @@ -372,4 +356,19 @@ public class IntervalStats { } return mStringCache.valueAt(index); } + + /** + * When an IntervalStats object is deserialized, if the object's version number + * is lower than current version number, optionally perform a upgrade. + */ + void upgradeIfNeeded() { + // We only uprade on majorVersion change, no need to upgrade on minorVersion change. + if (!(majorVersion < CURRENT_MAJOR_VERSION)) { + return; + } + /* + Optional upgrade code here. + */ + majorVersion = CURRENT_MAJOR_VERSION; + } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java index 30d303f426bf..8e1c06091605 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProto.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java @@ -21,13 +21,12 @@ import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; import android.util.ArrayMap; - import android.util.Slog; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.net.ProtocolException; import java.util.ArrayList; @@ -105,6 +104,7 @@ final class UsageStatsProto { stats = tempPackageIndex; break; case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS: + // Time attributes stored is an offset of the beginTime. stats.mLastTimeUsed = statsOut.beginTime + proto.readLong( IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS); break; @@ -113,7 +113,8 @@ final class UsageStatsProto { IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS); break; case (int) IntervalStatsProto.UsageStats.LAST_EVENT: - stats.mLastEvent = proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT); + stats.mLastEvent = + proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT); break; case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT: stats.mAppLaunchCount = proto.readInt( @@ -125,6 +126,15 @@ final class UsageStatsProto { loadChooserCounts(proto, stats); proto.end(chooserToken); break; + case (int) IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS: + // Time attributes stored is an offset of the beginTime. + stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + proto.readLong( + IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS); + break; + case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS: + stats.mTotalTimeForegroundServiceUsed = proto.readLong( + IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS); + break; } } if (stats.mLastTimeUsed == 0) { @@ -315,11 +325,18 @@ final class UsageStatsProto { + ") not found in IntervalStats string cache"); proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName); } + // Time attributes stored as an offset of the beginTime. proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS, usageStats.mLastTimeUsed - stats.beginTime); proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS, usageStats.mTotalTimeInForeground); - proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, usageStats.mLastEvent); + proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, + usageStats.mLastEvent); + // Time attributes stored as an offset of the beginTime. + proto.write(IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS, + usageStats.mLastTimeForegroundServiceUsed - stats.beginTime); + proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS, + usageStats.mTotalTimeForegroundServiceUsed); proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount); writeChooserCounts(proto, usageStats); proto.end(token); @@ -471,6 +488,14 @@ final class UsageStatsProto { statsOut.endTime = statsOut.beginTime + proto.readLong( IntervalStatsProto.END_TIME_MS); break; + case (int) IntervalStatsProto.MAJOR_VERSION: + statsOut.majorVersion = proto.readInt( + IntervalStatsProto.MAJOR_VERSION); + break; + case (int) IntervalStatsProto.MINOR_VERSION: + statsOut.minorVersion = proto.readInt( + IntervalStatsProto.MINOR_VERSION); + break; case (int) IntervalStatsProto.INTERACTIVE: loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE, statsOut.interactiveTracker); @@ -505,6 +530,7 @@ final class UsageStatsProto { // endTime not assigned, assume default value of 0 plus beginTime statsOut.endTime = statsOut.beginTime; } + statsOut.upgradeIfNeeded(); return; } } @@ -519,6 +545,8 @@ final class UsageStatsProto { public static void write(OutputStream out, IntervalStats stats) throws IOException { final ProtoOutputStream proto = new ProtoOutputStream(out); proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime); + proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion); + proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion); // String pool should be written before the rest of the usage stats writeStringPool(proto, stats); diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index a68f9d385ca5..d94062002dcd 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -15,19 +15,18 @@ */ package com.android.server.usage; -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - import android.app.usage.ConfigurationStats; -import android.app.usage.EventList; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; import android.util.ArrayMap; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.IOException; import java.net.ProtocolException; @@ -61,6 +60,7 @@ 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_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"; @@ -69,9 +69,12 @@ final class UsageStatsXmlV1 { private static final String STANDBY_BUCKET_ATTR = "standbyBucket"; private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount"; private static final String NOTIFICATION_CHANNEL_ATTR = "notificationChannel"; + private static final String MAJOR_VERSION_ATTR = "majorVersion"; + private static final String MINOR_VERSION_ATTR = "minorVersion"; // Time attributes stored as an offset of the beginTime. private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive"; + 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"; @@ -86,9 +89,14 @@ final class UsageStatsXmlV1 { // Apply the offset to the beginTime to find the absolute time. stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_ACTIVE_ATTR); + stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute( + parser, LAST_TIME_SERVICE_USED_ATTR); stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); + stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser, + TOTAL_TIME_SERVICE_USED_ATTR); stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); - stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR, 0); + stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR, + 0); int eventCode; while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) { final String tag = parser.getName(); @@ -206,9 +214,12 @@ final class UsageStatsXmlV1 { // Write the time offset. XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, usageStats.mLastTimeUsed - 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_SERVICE_USED_ATTR, + usageStats.mTotalTimeForegroundServiceUsed); XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); if (usageStats.mAppLaunchCount > 0) { XmlUtils.writeIntAttribute(xml, APP_LAUNCH_COUNT_ATTR, usageStats.mAppLaunchCount); @@ -339,6 +350,8 @@ final class UsageStatsXmlV1 { } statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR); + statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR); + statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR); int eventCode; int outerDepth = parser.getDepth(); @@ -391,6 +404,8 @@ final class UsageStatsXmlV1 { */ public static void write(XmlSerializer xml, IntervalStats stats) throws IOException { XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime); + XmlUtils.writeIntAttribute(xml, MAJOR_VERSION_ATTR, stats.majorVersion); + XmlUtils.writeIntAttribute(xml, MINOR_VERSION_ATTR, stats.minorVersion); writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveTracker.count, stats.interactiveTracker.duration); diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 1a8aba085d24..32875dab465f 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -22,9 +22,9 @@ import android.app.usage.EventStats; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; +import android.content.Context; import android.content.res.Configuration; import android.os.SystemClock; -import android.content.Context; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -129,11 +129,17 @@ class UserUsageStatsService { for (IntervalStats stat : mCurrentStats) { final int pkgCount = stat.packageStats.size(); for (int i = 0; i < pkgCount; i++) { - UsageStats pkgStats = stat.packageStats.valueAt(i); - if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND || - pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) { - stat.update(pkgStats.mPackageName, stat.lastTimeSaved, - UsageEvents.Event.END_OF_DAY); + 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(); } } @@ -218,7 +224,8 @@ class UserUsageStatsService { stats.updateKeyguardHidden(event.mTimeStamp); } break; default: { - stats.update(event.mPackage, event.mTimeStamp, event.mEventType); + stats.update(event.mPackage, event.getClassName(), + event.mTimeStamp, event.mEventType); if (incrementAppLaunch) { stats.incrementAppLaunchCount(event.mPackage); } @@ -481,25 +488,43 @@ class UserUsageStatsService { final long startTime = SystemClock.elapsedRealtime(); Slog.i(TAG, mLogPrefix + "Rolling over usage stats"); - // Finish any ongoing events with an END_OF_DAY event. Make a note of which components - // need a new CONTINUE_PREVIOUS_DAY entry. + // Finish any ongoing events with an END_OF_DAY or ROLLOVER_FOREGROUND_SERVICE event. + // 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 = + new ArrayMap<>(); + ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundService = + new ArrayMap<>(); for (IntervalStats stat : mCurrentStats) { final int pkgCount = stat.packageStats.size(); for (int i = 0; i < pkgCount; i++) { - UsageStats pkgStats = stat.packageStats.valueAt(i); - if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND || - pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) { + final UsageStats pkgStats = stat.packageStats.valueAt(i); + if (!pkgStats.mLastForegroundActivityEventMap.isEmpty() + || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) { + if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) { + continuePreviousDayForegroundActivity.put(pkgStats.mPackageName, + pkgStats.mLastForegroundActivityEventMap); + stat.update(pkgStats.mPackageName, null, + mDailyExpiryDate.getTimeInMillis() - 1, + UsageEvents.Event.END_OF_DAY); + } + if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) { + continuePreviousDayForegroundService.put(pkgStats.mPackageName, + pkgStats.mLastForegroundServiceEventMap); + stat.update(pkgStats.mPackageName, null, + mDailyExpiryDate.getTimeInMillis() - 1, + UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE); + } continuePreviousDay.add(pkgStats.mPackageName); - stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1, - UsageEvents.Event.END_OF_DAY); notifyStatsChanged(); } } - stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1); + stat.updateConfigurationStats(null, + mDailyExpiryDate.getTimeInMillis() - 1); stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1); } @@ -509,10 +534,27 @@ class UserUsageStatsService { final int continueCount = continuePreviousDay.size(); for (int i = 0; i < continueCount; i++) { - String name = continuePreviousDay.valueAt(i); + String pkgName = continuePreviousDay.valueAt(i); final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime; for (IntervalStats stat : mCurrentStats) { - stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY); + if (continuePreviousDayForegroundActivity.containsKey(pkgName)) { + final ArrayMap<String, Integer> foregroundActivityEventMap = + continuePreviousDayForegroundActivity.get(pkgName); + final int size = foregroundActivityEventMap.size(); + for (int j = 0; j < size; j++) { + stat.update(pkgName, foregroundActivityEventMap.keyAt(j), beginTime, + UsageEvents.Event.CONTINUE_PREVIOUS_DAY); + } + } + if (continuePreviousDayForegroundService.containsKey(pkgName)) { + final ArrayMap<String, Integer> foregroundServiceEventMap = + continuePreviousDayForegroundService.get(pkgName); + final int size = foregroundServiceEventMap.size(); + for (int j = 0; j < size; j++) { + stat.update(pkgName, foregroundServiceEventMap.keyAt(j), beginTime, + UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE); + } + } stat.updateConfigurationStats(previousConfig, beginTime); notifyStatsChanged(); } @@ -837,10 +879,18 @@ class UserUsageStatsService { return "MOVE_TO_BACKGROUND"; case UsageEvents.Event.MOVE_TO_FOREGROUND: return "MOVE_TO_FOREGROUND"; + case UsageEvents.Event.FOREGROUND_SERVICE_START: + return "FOREGROUND_SERVICE_START"; + case UsageEvents.Event.FOREGROUND_SERVICE_STOP: + return "FOREGROUND_SERVICE_STOP"; case UsageEvents.Event.END_OF_DAY: return "END_OF_DAY"; + case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE: + return "ROLLOVER_FOREGROUND_SERVICE"; case UsageEvents.Event.CONTINUE_PREVIOUS_DAY: return "CONTINUE_PREVIOUS_DAY"; + case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE: + return "CONTINUING_FOREGROUND_SERVICE"; case UsageEvents.Event.CONFIGURATION_CHANGE: return "CONFIGURATION_CHANGE"; case UsageEvents.Event.SYSTEM_INTERACTION: 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 8467bee819cb..be74a6d162ae 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 @@ -18,10 +18,6 @@ package com.android.frameworks.perftests.usage.tests; import static junit.framework.Assert.assertEquals; -import com.android.server.usage.UsageStatsDatabase; -import com.android.server.usage.UsageStatsDatabase.StatCombiner; -import com.android.server.usage.IntervalStats; - import android.app.usage.EventList; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager; @@ -29,10 +25,14 @@ import android.content.Context; import android.os.SystemClock; import android.perftests.utils.ManualBenchmarkState; import android.perftests.utils.PerfManualStatusReporter; -import android.support.test.filters.LargeTest; import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; +import com.android.server.usage.IntervalStats; +import com.android.server.usage.UsageStatsDatabase; +import com.android.server.usage.UsageStatsDatabase.StatCombiner; + import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; @@ -90,11 +90,13 @@ public class UsageStatsDatabasePerfTest { for (int pkg = 0; pkg < packageCount; pkg++) { UsageEvents.Event event = new UsageEvents.Event(); event.mPackage = "fake.package.name" + pkg; + event.mClass = event.mPackage + ".class1"; event.mTimeStamp = 1; event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND; for (int evt = 0; evt < eventsPerPackage; evt++) { intervalStats.events.insert(event); - intervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType); + intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, + event.mEventType); } } } |