821 lines
29 KiB
Java
821 lines
29 KiB
Java
/**
|
|
* 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.APP_COMPONENT_USED;
|
|
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 static android.app.usage.UsageEvents.Event.USER_INTERACTION;
|
|
|
|
import android.annotation.CurrentTimeMillisLong;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SystemApi;
|
|
import android.annotation.TestApi;
|
|
import android.compat.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.SparseArray;
|
|
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}
|
|
*/
|
|
public int mPackageToken = -1;
|
|
|
|
/**
|
|
* {@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;
|
|
|
|
/**
|
|
* Last time this package's component is used by a client package, measured in milliseconds
|
|
* since the epoch. Note that component usage is only reported in certain cases (e.g. broadcast
|
|
* receiver, service, content provider).
|
|
* See {@link UsageEvents.Event#APP_COMPONENT_USED}
|
|
* @hide
|
|
*/
|
|
public long mLastTimeComponentUsed;
|
|
|
|
/**
|
|
* {@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<String, Integer> mForegroundServices = new ArrayMap<>();
|
|
|
|
/**
|
|
* {@hide}
|
|
*/
|
|
public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts = new ArrayMap<>();
|
|
|
|
/**
|
|
* {@hide}
|
|
*/
|
|
public SparseArray<SparseIntArray> mChooserCountsObfuscated = new SparseArray<>();
|
|
|
|
/**
|
|
* {@hide}
|
|
*/
|
|
@TestApi
|
|
public UsageStats() {
|
|
}
|
|
|
|
public UsageStats(UsageStats stats) {
|
|
mPackageName = stats.mPackageName;
|
|
mBeginTimeStamp = stats.mBeginTimeStamp;
|
|
mEndTimeStamp = stats.mEndTimeStamp;
|
|
mLastTimeUsed = stats.mLastTimeUsed;
|
|
mLastTimeVisible = stats.mLastTimeVisible;
|
|
mLastTimeComponentUsed = stats.mLastTimeComponentUsed;
|
|
mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
|
|
mTotalTimeInForeground = stats.mTotalTimeInForeground;
|
|
mTotalTimeVisible = stats.mTotalTimeVisible;
|
|
mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed;
|
|
mLaunchCount = stats.mLaunchCount;
|
|
mAppLaunchCount = stats.mAppLaunchCount;
|
|
mLastEvent = stats.mLastEvent;
|
|
mActivities = stats.mActivities.clone();
|
|
mForegroundServices = new ArrayMap<>(stats.mForegroundServices);
|
|
mChooserCounts = new ArrayMap<>(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.
|
|
* <p/>
|
|
* 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.
|
|
* <p/>
|
|
* 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.
|
|
* <p/>
|
|
* 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. When in
|
|
* the foreground, the user is actively interacting with the app.
|
|
*/
|
|
public long getTotalTimeInForeground() {
|
|
return mTotalTimeInForeground;
|
|
}
|
|
|
|
/**
|
|
* Get the total time this package's activity is visible in the UI, measured in milliseconds.
|
|
* Note: An app may be visible but not considered foreground. Apps in the foreground must be
|
|
* visible, so visible time includes time in the foreground.
|
|
*/
|
|
public long getTotalTimeVisible() {
|
|
return mTotalTimeVisible;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Get the last time this package's component was used by a client package, measured in
|
|
* milliseconds since the epoch. Note that component usage is only reported for component
|
|
* bindings (e.g. broadcast receiver, service, content provider) and only when such a binding
|
|
* would cause an app to leave the stopped state.
|
|
* See {@link UsageEvents.Event#APP_COMPONENT_USED}
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@CurrentTimeMillisLong
|
|
public long getLastTimeAnyComponentUsed() {
|
|
return mLastTimeComponentUsed;
|
|
}
|
|
|
|
/**
|
|
* Returns the last time the package was used - defined by the latest of
|
|
* mLastTimeUsed, mLastTimeVisible, mLastTimeForegroundServiceUsed, or mLastTimeComponentUsed.
|
|
* @hide
|
|
*/
|
|
public long getLastTimePackageUsed() {
|
|
return Math.max(mLastTimeUsed,
|
|
Math.max(mLastTimeVisible,
|
|
Math.max(mLastTimeForegroundServiceUsed, mLastTimeComponentUsed)));
|
|
}
|
|
|
|
/**
|
|
* 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<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.
|
|
* @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);
|
|
mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed);
|
|
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<String, Integer> 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;
|
|
case USER_INTERACTION:
|
|
if (hasForegroundActivity()) {
|
|
incrementTimeUsed(timeStamp);
|
|
} else {
|
|
mLastTimeUsed = timeStamp;
|
|
}
|
|
if (hasVisibleActivity()) {
|
|
incrementTimeVisible(timeStamp);
|
|
} else {
|
|
mLastTimeVisible = timeStamp;
|
|
}
|
|
break;
|
|
case APP_COMPONENT_USED:
|
|
mLastTimeComponentUsed = 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(mLastTimeComponentUsed);
|
|
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<String, Integer> 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<String, Integer> eventMap) {
|
|
final Bundle bundle = new Bundle();
|
|
final int size = eventMap.size();
|
|
for (int i = 0; i < size; i++) {
|
|
bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i));
|
|
}
|
|
return bundle;
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
|
|
@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.mLastTimeComponentUsed = 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<String, Integer> 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<String, Integer> 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];
|
|
}
|
|
};
|
|
|
|
/** @hide */
|
|
// This class is used by the mainline test suite, so we have to keep these APIs around across
|
|
// releases. Consider making this class public to help external developers to write tests as
|
|
// well.
|
|
@TestApi
|
|
public static final class Builder {
|
|
private final UsageStats mUsageStats = new UsageStats();
|
|
|
|
@NonNull
|
|
public UsageStats build() {
|
|
return mUsageStats;
|
|
}
|
|
|
|
@NonNull
|
|
public Builder setPackageName(@Nullable String packageName) {
|
|
mUsageStats.mPackageName = packageName;
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
public Builder setFirstTimeStamp(long firstTimeStamp) {
|
|
mUsageStats.mBeginTimeStamp = firstTimeStamp;
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
public Builder setLastTimeStamp(long lastTimeStamp) {
|
|
mUsageStats.mEndTimeStamp = lastTimeStamp;
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
public Builder setTotalTimeInForeground(long totalTimeInForeground) {
|
|
mUsageStats.mTotalTimeInForeground = totalTimeInForeground;
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
public Builder setLastTimeUsed(long lastTimeUsed) {
|
|
mUsageStats.mLastTimeUsed = lastTimeUsed;
|
|
return this;
|
|
}
|
|
}
|
|
}
|