All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.app.usage.UsageStats Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/**
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package android.app.usage;

import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;

import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.SparseIntArray;

/**
 * Contains usage statistics for an app package for a specific
 * time range.
 */
public final class UsageStats implements Parcelable {

    /**
     * {@hide}
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public String mPackageName;

    /**
     * {@hide}
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public long mBeginTimeStamp;

    /**
     * {@hide}
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public long mEndTimeStamp;

    /**
     * Last time an activity is at foreground (have focus), this is corresponding to
     * {@link android.app.usage.UsageEvents.Event#ACTIVITY_RESUMED} event.
     * {@hide}
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public long mLastTimeUsed;

    /**
     * Last time an activity is visible.
     * @hide
     */
    public long mLastTimeVisible;

    /**
     * Total time this package's activity is in foreground.
     * {@hide}
     */
    @UnsupportedAppUsage
    public long mTotalTimeInForeground;

    /**
     * Total time this package's activity is visible.
     * {@hide}
     */
    public long mTotalTimeVisible;

    /**
     * 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
    public int mLaunchCount;

    /**
     * {@hide}
     */
    public int mAppLaunchCount;

    /** Last activity ACTIVITY_RESUMED or ACTIVITY_PAUSED event.
     * {@hide}
     * @deprecated use {@link #mActivities} instead.
     */
    @UnsupportedAppUsage
    @Deprecated
    public int mLastEvent;

    /**
     * Key is instanceId of the activity (ActivityRecode appToken hashCode)..
     * Value is this activity's last event, one of ACTIVITY_RESUMED, ACTIVITY_PAUSED or
     * ACTIVITY_STOPPED.
     * {@hide}
     */
    public SparseIntArray mActivities = new SparseIntArray();
    /**
     * If a foreground service is started, it has one entry in this map.
     * When a foreground service is stopped, it is removed from this set.
     * Key is foreground service class name.
     * Value is the foreground service's last event, it is FOREGROUND_SERVICE_START.
     * {@hide}
     */
    public ArrayMap mForegroundServices = new ArrayMap<>();

    /**
     * {@hide}
     */
    public ArrayMap> mChooserCounts = new ArrayMap<>();

    /**
     * {@hide}
     */
    public UsageStats() {
    }

    public UsageStats(UsageStats stats) {
        mPackageName = stats.mPackageName;
        mBeginTimeStamp = stats.mBeginTimeStamp;
        mEndTimeStamp = stats.mEndTimeStamp;
        mLastTimeUsed = stats.mLastTimeUsed;
        mLastTimeVisible = stats.mLastTimeVisible;
        mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
        mTotalTimeInForeground = stats.mTotalTimeInForeground;
        mTotalTimeVisible = stats.mTotalTimeVisible;
        mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed;
        mLaunchCount = stats.mLaunchCount;
        mAppLaunchCount = stats.mAppLaunchCount;
        mLastEvent = stats.mLastEvent;
        mActivities = stats.mActivities;
        mForegroundServices = stats.mForegroundServices;
        mChooserCounts = stats.mChooserCounts;
    }

    /**
     * {@hide}
     */
    public UsageStats getObfuscatedForInstantApp() {
        final UsageStats ret = new UsageStats(this);

        ret.mPackageName = UsageEvents.INSTANT_APP_PACKAGE_NAME;

        return ret;
    }

    public String getPackageName() {
        return mPackageName;
    }

    /**
     * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents,
     * measured in milliseconds since the epoch.
     * 

* See {@link System#currentTimeMillis()}. */ public long getFirstTimeStamp() { return mBeginTimeStamp; } /** * Get the end of the time range this {@link android.app.usage.UsageStats} represents, * measured in milliseconds since the epoch. *

* See {@link System#currentTimeMillis()}. */ public long getLastTimeStamp() { return mEndTimeStamp; } /** * Get the last time this package's activity was used, measured in milliseconds since the epoch. *

* See {@link System#currentTimeMillis()}. */ public long getLastTimeUsed() { return mLastTimeUsed; } /** * Get the last time this package's activity is visible in the UI, measured in milliseconds * since the epoch. */ public long getLastTimeVisible() { return mLastTimeVisible; } /** * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { return mTotalTimeInForeground; } /** * Get the total time this package's activity is visible in the UI, measured in milliseconds. */ public long getTotalTimeVisible() { return mTotalTimeVisible; } /** * Get the last time this package's foreground service was used, measured in milliseconds since * the epoch. *

* 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 */ @SystemApi public int getAppLaunchCount() { return mAppLaunchCount; } private void mergeEventMap(SparseIntArray left, SparseIntArray right) { final int size = right.size(); for (int i = 0; i < size; i++) { final int instanceId = right.keyAt(i); final int event = right.valueAt(i); final int index = left.indexOfKey(instanceId); if (index >= 0) { left.put(instanceId, Math.max(left.valueAt(index), event)); } else { left.put(instanceId, event); } } } private void mergeEventMap(ArrayMap left, ArrayMap 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. * @param right The {@link UsageStats} object to merge into this one. * @throws java.lang.IllegalArgumentException if the package names of the two * {@link UsageStats} objects are different. */ public void add(UsageStats right) { if (!mPackageName.equals(right.mPackageName)) { throw new IllegalArgumentException("Can't merge UsageStats for package '" + mPackageName + "' with UsageStats for package '" + right.mPackageName + "'."); } // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with // regards to their mEndTimeStamp. if (right.mBeginTimeStamp > mBeginTimeStamp) { // Even though incoming UsageStat begins after this one, its last time used fields // may somehow be empty or chronologically preceding the older UsageStat. mergeEventMap(mActivities, right.mActivities); mergeEventMap(mForegroundServices, right.mForegroundServices); mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed); mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible); mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed, right.mLastTimeForegroundServiceUsed); } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; mTotalTimeVisible += right.mTotalTimeVisible; mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed; mLaunchCount += right.mLaunchCount; mAppLaunchCount += right.mAppLaunchCount; if (mChooserCounts == null) { mChooserCounts = right.mChooserCounts; } else if (right.mChooserCounts != null) { final int chooserCountsSize = right.mChooserCounts.size(); for (int i = 0; i < chooserCountsSize; i++) { String action = right.mChooserCounts.keyAt(i); ArrayMap counts = right.mChooserCounts.valueAt(i); if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) { mChooserCounts.put(action, counts); continue; } final int annotationSize = counts.size(); for (int j = 0; j < annotationSize; j++) { String key = counts.keyAt(j); int rightValue = counts.valueAt(j); int leftValue = mChooserCounts.get(action).getOrDefault(key, 0); mChooserCounts.get(action).put(key, leftValue + rightValue); } } } } /** * Tell if any activity is in foreground. * @return */ private boolean hasForegroundActivity() { final int size = mActivities.size(); for (int i = 0; i < size; i++) { if (mActivities.valueAt(i) == ACTIVITY_RESUMED) { return true; } } return false; } /** * Tell if any activity is visible. * @return */ private boolean hasVisibleActivity() { final int size = mActivities.size(); for (int i = 0; i < size; i++) { final int type = mActivities.valueAt(i); if (type == ACTIVITY_RESUMED || type == ACTIVITY_PAUSED) { return true; } } return false; } /** * Tell if any foreground service is started. * @return */ private boolean anyForegroundServiceStarted() { return !mForegroundServices.isEmpty(); } /** * Increment total time in foreground and update last time in foreground. * @param timeStamp current timestamp. */ private void incrementTimeUsed(long timeStamp) { if (timeStamp > mLastTimeUsed) { mTotalTimeInForeground += timeStamp - mLastTimeUsed; mLastTimeUsed = timeStamp; } } /** * Increment total time visible and update last time visible. * @param timeStamp current timestmap. */ private void incrementTimeVisible(long timeStamp) { if (timeStamp > mLastTimeVisible) { mTotalTimeVisible += timeStamp - mLastTimeVisible; mLastTimeVisible = timeStamp; } } /** * Increment total time foreground service is used and update last time foreground service is * used. * @param timeStamp current timestamp. */ private void incrementServiceTimeUsed(long timeStamp) { if (timeStamp > mLastTimeForegroundServiceUsed) { mTotalTimeForegroundServiceUsed += timeStamp - mLastTimeForegroundServiceUsed; mLastTimeForegroundServiceUsed = timeStamp; } } /** * 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. * @param instanceId hashCode of the ActivityRecord's appToken. * @hide */ private void updateActivity(String className, long timeStamp, int eventType, int instanceId) { if (eventType != ACTIVITY_RESUMED && eventType != ACTIVITY_PAUSED && eventType != ACTIVITY_STOPPED && eventType != ACTIVITY_DESTROYED) { return; } // update usage. final int index = mActivities.indexOfKey(instanceId); if (index >= 0) { final int lastEvent = mActivities.valueAt(index); switch (lastEvent) { case ACTIVITY_RESUMED: incrementTimeUsed(timeStamp); incrementTimeVisible(timeStamp); break; case ACTIVITY_PAUSED: incrementTimeVisible(timeStamp); break; default: break; } } // update current event. switch(eventType) { case ACTIVITY_RESUMED: if (!hasVisibleActivity()) { // this is the first visible activity. mLastTimeUsed = timeStamp; mLastTimeVisible = timeStamp; } else if (!hasForegroundActivity()) { // this is the first foreground activity. mLastTimeUsed = timeStamp; } mActivities.put(instanceId, eventType); break; case ACTIVITY_PAUSED: if (!hasVisibleActivity()) { // this is the first visible activity. mLastTimeVisible = timeStamp; } mActivities.put(instanceId, eventType); break; case ACTIVITY_STOPPED: case ACTIVITY_DESTROYED: // remove activity from the map. mActivities.delete(instanceId); break; default: break; } } /** * 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) { return; } final Integer lastEvent = mForegroundServices.get(className); // update usage. if (lastEvent != null) { switch (lastEvent) { case FOREGROUND_SERVICE_START: case CONTINUING_FOREGROUND_SERVICE: incrementServiceTimeUsed(timeStamp); break; default: break; } } // update current event. switch (eventType) { case FOREGROUND_SERVICE_START: if (!anyForegroundServiceStarted()) { mLastTimeForegroundServiceUsed = timeStamp; } mForegroundServices.put(className, eventType); break; case FOREGROUND_SERVICE_STOP: mForegroundServices.remove(className); break; default: break; } } /** * Update the UsageStats by a activity or foreground service event. * @param className class name of a activity or foreground service, could be null to if this * is sent to all activities/services in this package. * @param timeStamp Epoch timestamp in milliseconds. * @param eventType event type as in {@link UsageEvents.Event} * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken. * if className is not an activity, instanceId is not used. * @hide */ public void update(String className, long timeStamp, int eventType, int instanceId) { switch(eventType) { case ACTIVITY_RESUMED: case ACTIVITY_PAUSED: case ACTIVITY_STOPPED: case ACTIVITY_DESTROYED: updateActivity(className, timeStamp, eventType, instanceId); break; case END_OF_DAY: // END_OF_DAY updates all activities. if (hasForegroundActivity()) { incrementTimeUsed(timeStamp); } if (hasVisibleActivity()) { incrementTimeVisible(timeStamp); } break; case FOREGROUND_SERVICE_START: case FOREGROUND_SERVICE_STOP: updateForegroundService(className, timeStamp, eventType); break; case ROLLOVER_FOREGROUND_SERVICE: // ROLLOVER_FOREGROUND_SERVICE updates all foreground services. if (anyForegroundServiceStarted()) { incrementServiceTimeUsed(timeStamp); } break; case CONTINUING_FOREGROUND_SERVICE: mLastTimeForegroundServiceUsed = timeStamp; mForegroundServices.put(className, eventType); break; case DEVICE_SHUTDOWN: case FLUSH_TO_DISK: // update usage of all active activities/services. if (hasForegroundActivity()) { incrementTimeUsed(timeStamp); } if (hasVisibleActivity()) { incrementTimeVisible(timeStamp); } if (anyForegroundServiceStarted()) { incrementServiceTimeUsed(timeStamp); } break; default: break; } mEndTimeStamp = timeStamp; if (eventType == ACTIVITY_RESUMED) { mLaunchCount += 1; } } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackageName); dest.writeLong(mBeginTimeStamp); dest.writeLong(mEndTimeStamp); dest.writeLong(mLastTimeUsed); dest.writeLong(mLastTimeVisible); dest.writeLong(mLastTimeForegroundServiceUsed); dest.writeLong(mTotalTimeInForeground); dest.writeLong(mTotalTimeVisible); dest.writeLong(mTotalTimeForegroundServiceUsed); dest.writeInt(mLaunchCount); dest.writeInt(mAppLaunchCount); dest.writeInt(mLastEvent); Bundle allCounts = new Bundle(); if (mChooserCounts != null) { final int chooserCountSize = mChooserCounts.size(); for (int i = 0; i < chooserCountSize; i++) { String action = mChooserCounts.keyAt(i); ArrayMap counts = mChooserCounts.valueAt(i); Bundle currentCounts = new Bundle(); final int annotationSize = counts.size(); for (int j = 0; j < annotationSize; j++) { currentCounts.putInt(counts.keyAt(j), counts.valueAt(j)); } allCounts.putBundle(action, currentCounts); } } dest.writeBundle(allCounts); writeSparseIntArray(dest, mActivities); dest.writeBundle(eventMapToBundle(mForegroundServices)); } private void writeSparseIntArray(Parcel dest, SparseIntArray arr) { final int size = arr.size(); dest.writeInt(size); for (int i = 0; i < size; i++) { dest.writeInt(arr.keyAt(i)); dest.writeInt(arr.valueAt(i)); } } private Bundle eventMapToBundle(ArrayMap eventMap) { final Bundle bundle = new Bundle(); final int size = eventMap.size(); for (int i = 0; i < size; i++) { bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i)); } return bundle; } public static final @android.annotation.NonNull Creator CREATOR = new Creator() { @Override public UsageStats createFromParcel(Parcel in) { UsageStats stats = new UsageStats(); stats.mPackageName = in.readString(); stats.mBeginTimeStamp = in.readLong(); stats.mEndTimeStamp = in.readLong(); stats.mLastTimeUsed = in.readLong(); stats.mLastTimeVisible = in.readLong(); stats.mLastTimeForegroundServiceUsed = in.readLong(); stats.mTotalTimeInForeground = in.readLong(); stats.mTotalTimeVisible = in.readLong(); stats.mTotalTimeForegroundServiceUsed = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mAppLaunchCount = in.readInt(); stats.mLastEvent = in.readInt(); Bundle allCounts = in.readBundle(); if (allCounts != null) { stats.mChooserCounts = new ArrayMap<>(); for (String action : allCounts.keySet()) { if (!stats.mChooserCounts.containsKey(action)) { ArrayMap newCounts = new ArrayMap<>(); stats.mChooserCounts.put(action, newCounts); } Bundle currentCounts = allCounts.getBundle(action); if (currentCounts != null) { for (String key : currentCounts.keySet()) { int value = currentCounts.getInt(key); if (value > 0) { stats.mChooserCounts.get(action).put(key, value); } } } } } readSparseIntArray(in, stats.mActivities); readBundleToEventMap(in.readBundle(), stats.mForegroundServices); return stats; } private void readSparseIntArray(Parcel in, SparseIntArray arr) { final int size = in.readInt(); for (int i = 0; i < size; i++) { final int key = in.readInt(); final int value = in.readInt(); arr.put(key, value); } } private void readBundleToEventMap(Bundle bundle, ArrayMap eventMap) { 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]; } }; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy