diff options
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); } } } |