Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e361a23b authored by Hui Yu's avatar Hui Yu
Browse files

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
parent 581cccdd
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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;
+44 −3
Original line number Diff line number Diff line
@@ -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;

@@ -165,11 +173,44 @@ 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;
+271 −5
Original line number Diff line number Diff line
@@ -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,18 +57,31 @@ 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}
     */
@@ -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()}.
     */
@@ -151,6 +197,23 @@ public final class UsageStats implements Parcelable {
        return mTotalTimeInForeground;
    }

    /**
     * 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.
@@ -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];
+4 −1
Original line number Diff line number Diff line
@@ -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;

+6 −0
Original line number Diff line number Diff line
@@ -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;
Loading