Merge "Work on issue #74404949: Screen state usage API" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
00f3c4333e
@@ -7334,6 +7334,20 @@ package android.app.usage {
|
||||
field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
|
||||
}
|
||||
|
||||
public final class EventStats implements android.os.Parcelable {
|
||||
ctor public EventStats(android.app.usage.EventStats);
|
||||
method public void add(android.app.usage.EventStats);
|
||||
method public int describeContents();
|
||||
method public int getCount();
|
||||
method public int getEventType();
|
||||
method public long getFirstTimeStamp();
|
||||
method public long getLastTimeStamp();
|
||||
method public long getLastTimeUsed();
|
||||
method public long getTotalTime();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.app.usage.EventStats> CREATOR;
|
||||
}
|
||||
|
||||
public final class ExternalStorageStats implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public long getAppBytes();
|
||||
@@ -7439,6 +7453,8 @@ package android.app.usage {
|
||||
field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
|
||||
field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
|
||||
field public static final int NONE = 0; // 0x0
|
||||
field public static final int SCREEN_INTERACTIVE = 15; // 0xf
|
||||
field public static final int SCREEN_NON_INTERACTIVE = 16; // 0x10
|
||||
field public static final int SHORTCUT_INVOCATION = 8; // 0x8
|
||||
field public static final int STANDBY_BUCKET_CHANGED = 11; // 0xb
|
||||
field public static final int USER_INTERACTION = 7; // 0x7
|
||||
@@ -7462,6 +7478,7 @@ package android.app.usage {
|
||||
method public boolean isAppInactive(java.lang.String);
|
||||
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
|
||||
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
|
||||
method public java.util.List<android.app.usage.EventStats> queryEventStats(int, long, long);
|
||||
method public android.app.usage.UsageEvents queryEvents(long, long);
|
||||
method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
|
||||
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
|
||||
|
||||
182
core/java/android/app/usage/EventStats.java
Normal file
182
core/java/android/app/usage/EventStats.java
Normal file
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Contains usage statistics for an event type for a specific
|
||||
* time range.
|
||||
*/
|
||||
public final class EventStats implements Parcelable {
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public int mEventType;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mBeginTimeStamp;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mEndTimeStamp;
|
||||
|
||||
/**
|
||||
* Last time used by the user with an explicit action (notification, activity launch).
|
||||
* {@hide}
|
||||
*/
|
||||
public long mLastTimeUsed;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mTotalTime;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public int mCount;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public EventStats() {
|
||||
}
|
||||
|
||||
public EventStats(EventStats stats) {
|
||||
mEventType = stats.mEventType;
|
||||
mBeginTimeStamp = stats.mBeginTimeStamp;
|
||||
mEndTimeStamp = stats.mEndTimeStamp;
|
||||
mLastTimeUsed = stats.mLastTimeUsed;
|
||||
mTotalTime = stats.mTotalTime;
|
||||
mCount = stats.mCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of event this is usage for. May be one of the event
|
||||
* constants in {@link UsageEvents.Event}.
|
||||
*/
|
||||
public int getEventType() {
|
||||
return mEventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the beginning of the time range this {@link android.app.usage.EventStats} 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.EventStats} represents,
|
||||
* measured in milliseconds since the epoch.
|
||||
* <p/>
|
||||
* See {@link System#currentTimeMillis()}.
|
||||
*/
|
||||
public long getLastTimeStamp() {
|
||||
return mEndTimeStamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time this event was used, measured in milliseconds since the epoch.
|
||||
* <p/>
|
||||
* See {@link System#currentTimeMillis()}.
|
||||
*/
|
||||
public long getLastTimeUsed() {
|
||||
return mLastTimeUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times that this event occurred over the interval.
|
||||
*/
|
||||
public int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total time this event was active, measured in milliseconds.
|
||||
*/
|
||||
public long getTotalTime() {
|
||||
return mTotalTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the statistics from the right {@link EventStats} to the left. The event type for
|
||||
* both {@link UsageStats} objects must be the same.
|
||||
* @param right The {@link EventStats} object to merge into this one.
|
||||
* @throws java.lang.IllegalArgumentException if the event types of the two
|
||||
* {@link UsageStats} objects are different.
|
||||
*/
|
||||
public void add(EventStats right) {
|
||||
if (mEventType != right.mEventType) {
|
||||
throw new IllegalArgumentException("Can't merge EventStats for event #"
|
||||
+ mEventType + " with EventStats for event #" + right.mEventType);
|
||||
}
|
||||
|
||||
// We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
|
||||
// regards to their mEndTimeStamp.
|
||||
if (right.mBeginTimeStamp > mBeginTimeStamp) {
|
||||
mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
|
||||
}
|
||||
mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
|
||||
mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
|
||||
mTotalTime += right.mTotalTime;
|
||||
mCount += right.mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mEventType);
|
||||
dest.writeLong(mBeginTimeStamp);
|
||||
dest.writeLong(mEndTimeStamp);
|
||||
dest.writeLong(mLastTimeUsed);
|
||||
dest.writeLong(mTotalTime);
|
||||
dest.writeInt(mCount);
|
||||
}
|
||||
|
||||
public static final Creator<EventStats> CREATOR = new Creator<EventStats>() {
|
||||
@Override
|
||||
public EventStats createFromParcel(Parcel in) {
|
||||
EventStats stats = new EventStats();
|
||||
stats.mEventType = in.readInt();
|
||||
stats.mBeginTimeStamp = in.readLong();
|
||||
stats.mEndTimeStamp = in.readLong();
|
||||
stats.mLastTimeUsed = in.readLong();
|
||||
stats.mTotalTime = in.readLong();
|
||||
stats.mCount = in.readInt();
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStats[] newArray(int size) {
|
||||
return new EventStats[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -32,6 +32,8 @@ interface IUsageStatsManager {
|
||||
String callingPackage);
|
||||
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
|
||||
String callingPackage);
|
||||
ParceledListSlice queryEventStats(int bucketType, long beginTime, long endTime,
|
||||
String callingPackage);
|
||||
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
|
||||
UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage);
|
||||
void setAppInactive(String packageName, boolean inactive, int userId);
|
||||
|
||||
@@ -139,6 +139,19 @@ public final class UsageEvents implements Parcelable {
|
||||
@SystemApi
|
||||
public static final int SLICE_PINNED = 14;
|
||||
|
||||
/**
|
||||
* An event type denoting that the screen has gone in to an interactive state (turned
|
||||
* on for full user interaction, not ambient display or other non-interactive state).
|
||||
*/
|
||||
public static final int SCREEN_INTERACTIVE = 15;
|
||||
|
||||
/**
|
||||
* An event type denoting that the screen has gone in to a non-interactive state
|
||||
* (completely turned off or turned on only in a non-interactive state like ambient
|
||||
* display).
|
||||
*/
|
||||
public static final int SCREEN_NON_INTERACTIVE = 16;
|
||||
|
||||
/** @hide */
|
||||
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
|
||||
|
||||
|
||||
@@ -300,6 +300,44 @@ public final class UsageStatsManager {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets aggregated event stats for the given time range, aggregated by the specified interval.
|
||||
* <p>The returned list will contain a {@link EventStats} object for each event type that
|
||||
* is being aggregated and has data for an interval that is a subset of the time range given.
|
||||
*
|
||||
* <p>The current event types that will be aggregated here are:</p>
|
||||
* <ul>
|
||||
* <li>{@link UsageEvents.Event#SCREEN_INTERACTIVE}</li>
|
||||
* <li>{@link UsageEvents.Event#SCREEN_NON_INTERACTIVE}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
|
||||
*
|
||||
* @param intervalType The time interval by which the stats are aggregated.
|
||||
* @param beginTime The inclusive beginning of the range of stats to include in the results.
|
||||
* @param endTime The exclusive end of the range of stats to include in the results.
|
||||
* @return A list of {@link EventStats}
|
||||
*
|
||||
* @see #INTERVAL_DAILY
|
||||
* @see #INTERVAL_WEEKLY
|
||||
* @see #INTERVAL_MONTHLY
|
||||
* @see #INTERVAL_YEARLY
|
||||
* @see #INTERVAL_BEST
|
||||
*/
|
||||
public List<EventStats> queryEventStats(int intervalType, long beginTime, long endTime) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
ParceledListSlice<EventStats> slice = mService.queryEventStats(intervalType, beginTime,
|
||||
endTime, mContext.getOpPackageName());
|
||||
if (slice != null) {
|
||||
return slice.getList();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// fallthrough and return the empty list.
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for events in the given time range. Events are only kept by the system for a few
|
||||
* days.
|
||||
|
||||
@@ -2803,8 +2803,8 @@ public final class Parcel {
|
||||
Class<?> parcelableClass = Class.forName(name, false /* initialize */,
|
||||
parcelableClassLoader);
|
||||
if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
|
||||
throw new BadParcelableException("Parcelable protocol requires that the "
|
||||
+ "class implements Parcelable");
|
||||
throw new BadParcelableException("Parcelable protocol requires subclassing "
|
||||
+ "from Parcelable on class " + name);
|
||||
}
|
||||
Field f = parcelableClass.getField("CREATOR");
|
||||
if ((f.getModifiers() & Modifier.STATIC) == 0) {
|
||||
|
||||
@@ -13050,6 +13050,22 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return mSleeping;
|
||||
}
|
||||
|
||||
void reportGlobalUsageEventLocked(int event) {
|
||||
mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
|
||||
int[] profiles = mUserController.getCurrentProfileIds();
|
||||
if (profiles != null) {
|
||||
for (int i = profiles.length - 1; i >= 0; i--) {
|
||||
mUsageStatsService.reportEvent((String)null, profiles[i], event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reportCurWakefulnessUsageEventLocked() {
|
||||
reportGlobalUsageEventLocked(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
|
||||
? UsageEvents.Event.SCREEN_INTERACTIVE
|
||||
: UsageEvents.Event.SCREEN_NON_INTERACTIVE);
|
||||
}
|
||||
|
||||
void onWakefulnessChanged(int wakefulness) {
|
||||
synchronized(this) {
|
||||
boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
|
||||
@@ -13059,6 +13075,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
if (wasAwake != isAwake) {
|
||||
// Also update state in a special way for running foreground services UI.
|
||||
mServices.updateScreenStateLocked(isAwake);
|
||||
reportCurWakefulnessUsageEventLocked();
|
||||
mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.app.Dialog;
|
||||
import android.app.IStopUserCallback;
|
||||
import android.app.IUserSwitchObserver;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.content.Context;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.Intent;
|
||||
@@ -959,6 +960,8 @@ class UserController implements Handler.Callback {
|
||||
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
|
||||
}
|
||||
if (foreground) {
|
||||
// Make sure the old user is no longer considering the display to be on.
|
||||
mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
|
||||
synchronized (mLock) {
|
||||
mCurrentUserId = userId;
|
||||
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
|
||||
@@ -966,6 +969,7 @@ class UserController implements Handler.Callback {
|
||||
mInjector.updateUserConfiguration();
|
||||
updateCurrentProfileIds();
|
||||
mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
|
||||
mInjector.reportCurWakefulnessUsageEvent();
|
||||
// Once the internal notion of the active user has switched, we lock the device
|
||||
// with the option to show the user switcher on the keyguard.
|
||||
if (mUserSwitchUiEnabled) {
|
||||
@@ -2183,6 +2187,18 @@ class UserController implements Handler.Callback {
|
||||
d.show();
|
||||
}
|
||||
|
||||
void reportGlobalUsageEventLocked(int event) {
|
||||
synchronized (mService) {
|
||||
mService.reportGlobalUsageEventLocked(event);
|
||||
}
|
||||
}
|
||||
|
||||
void reportCurWakefulnessUsageEvent() {
|
||||
synchronized (mService) {
|
||||
mService.reportCurWakefulnessUsageEventLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void stackSupervisorRemoveUser(int userId) {
|
||||
synchronized (mService) {
|
||||
mService.mStackSupervisor.removeUserLocked(userId);
|
||||
|
||||
@@ -389,6 +389,14 @@ public class UserControllerTest extends AndroidTestCase {
|
||||
sentIntents.add(intent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
void reportGlobalUsageEventLocked(int event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
void reportCurWakefulnessUsageEvent() {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestHandler extends Handler {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.EventStats;
|
||||
import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
@@ -23,10 +24,18 @@ import android.content.res.Configuration;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class IntervalStats {
|
||||
public long beginTime;
|
||||
public long endTime;
|
||||
public long lastTimeSaved;
|
||||
public long lastInteractiveTime;
|
||||
public long lastNonInteractiveTime;
|
||||
public long interactiveDuration;
|
||||
public int interactiveCount;
|
||||
public long nonInteractiveDuration;
|
||||
public int nonInteractiveCount;
|
||||
public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
|
||||
public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
|
||||
public Configuration activeConfiguration;
|
||||
@@ -171,6 +180,60 @@ class IntervalStats {
|
||||
usageStats.mAppLaunchCount += 1;
|
||||
}
|
||||
|
||||
private void commitInteractiveTime(long timeStamp) {
|
||||
if (lastInteractiveTime != 0) {
|
||||
interactiveDuration += timeStamp - lastInteractiveTime;
|
||||
lastInteractiveTime = 0;
|
||||
}
|
||||
if (lastNonInteractiveTime != 0) {
|
||||
nonInteractiveDuration += timeStamp - lastNonInteractiveTime;
|
||||
lastNonInteractiveTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void commitTime(long timeStamp) {
|
||||
commitInteractiveTime(timeStamp);
|
||||
}
|
||||
|
||||
void updateScreenInteractive(long timeStamp) {
|
||||
if (lastInteractiveTime != 0) {
|
||||
// Already interactive, just keep running.
|
||||
return;
|
||||
}
|
||||
commitInteractiveTime(timeStamp);
|
||||
lastInteractiveTime = timeStamp;
|
||||
interactiveCount++;
|
||||
}
|
||||
|
||||
void updateScreenNonInteractive(long timeStamp) {
|
||||
if (lastNonInteractiveTime != 0) {
|
||||
// Already non-interactive, just keep running.
|
||||
return;
|
||||
}
|
||||
commitInteractiveTime(timeStamp);
|
||||
lastNonInteractiveTime = timeStamp;
|
||||
nonInteractiveCount++;
|
||||
}
|
||||
|
||||
private void addOneEventStats(List<EventStats> out, int event, int count, long duration) {
|
||||
if (count != 0 || duration != 0) {
|
||||
EventStats ev = new EventStats();
|
||||
ev.mEventType = event;
|
||||
ev.mCount = count;
|
||||
ev.mTotalTime = duration;
|
||||
ev.mBeginTimeStamp = beginTime;
|
||||
ev.mEndTimeStamp = endTime;
|
||||
out.add(ev);
|
||||
}
|
||||
}
|
||||
|
||||
void addEventStatsTo(List<EventStats> out) {
|
||||
addOneEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE, interactiveCount,
|
||||
interactiveDuration);
|
||||
addOneEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE, nonInteractiveCount,
|
||||
nonInteractiveDuration);
|
||||
}
|
||||
|
||||
private String getCachedStringRef(String str) {
|
||||
final int index = mStringCache.indexOf(str);
|
||||
if (index < 0) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.admin.DeviceAdminInfo;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.EventStats;
|
||||
import android.app.usage.IUsageStatsManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
@@ -483,6 +484,23 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
List<EventStats> queryEventStats(int userId, int bucketType, long beginTime,
|
||||
long endTime) {
|
||||
synchronized (mLock) {
|
||||
final long timeNow = checkAndGetTimeLocked();
|
||||
if (!validRange(timeNow, beginTime, endTime)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final UserUsageStatsService service =
|
||||
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
|
||||
return service.queryEventStats(bucketType, beginTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
@@ -712,6 +730,28 @@ public class UsageStatsService extends SystemService implements
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParceledListSlice<EventStats> queryEventStats(int bucketType,
|
||||
long beginTime, long endTime, String callingPackage) throws RemoteException {
|
||||
if (!hasPermission(callingPackage)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int userId = UserHandle.getCallingUserId();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final List<EventStats> results =
|
||||
UsageStatsService.this.queryEventStats(userId, bucketType,
|
||||
beginTime, endTime);
|
||||
if (results != null) {
|
||||
return new ParceledListSlice<>(results);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
|
||||
if (!hasPermission(callingPackage)) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
@@ -37,6 +38,9 @@ import java.net.ProtocolException;
|
||||
final class UsageStatsXmlV1 {
|
||||
private static final String TAG = "UsageStatsXmlV1";
|
||||
|
||||
private static final String INTERACTIVE_TAG = "interactive";
|
||||
private static final String NON_INTERACTIVE_TAG = "non-interactive";
|
||||
|
||||
private static final String PACKAGES_TAG = "packages";
|
||||
private static final String PACKAGE_TAG = "package";
|
||||
|
||||
@@ -99,6 +103,14 @@ final class UsageStatsXmlV1 {
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<Integer, Long> loadCountAndTime(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
int count = XmlUtils.readIntAttribute(parser, COUNT_ATTR, 0);
|
||||
long time = XmlUtils.readLongAttribute(parser, TIME_ATTR, 0);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
return new Pair<>(count, time);
|
||||
}
|
||||
|
||||
private static void loadChooserCounts(
|
||||
XmlPullParser parser, UsageStats usageStats, String action)
|
||||
throws XmlPullParserException, IOException {
|
||||
@@ -202,6 +214,14 @@ final class UsageStatsXmlV1 {
|
||||
xml.endTag(null, PACKAGE_TAG);
|
||||
}
|
||||
|
||||
private static void writeCountAndTime(XmlSerializer xml, String tag, int count, long time)
|
||||
throws IOException {
|
||||
xml.startTag(null, tag);
|
||||
XmlUtils.writeIntAttribute(xml, COUNT_ATTR, count);
|
||||
XmlUtils.writeLongAttribute(xml, TIME_ATTR, time);
|
||||
xml.endTag(null, tag);
|
||||
}
|
||||
|
||||
private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
|
||||
throws IOException {
|
||||
if (usageStats == null || usageStats.mChooserCounts == null ||
|
||||
@@ -320,6 +340,18 @@ final class UsageStatsXmlV1 {
|
||||
|
||||
final String tag = parser.getName();
|
||||
switch (tag) {
|
||||
case INTERACTIVE_TAG: {
|
||||
Pair<Integer, Long> result = loadCountAndTime(parser);
|
||||
statsOut.interactiveCount = result.first;
|
||||
statsOut.interactiveDuration = result.second;
|
||||
} break;
|
||||
|
||||
case NON_INTERACTIVE_TAG: {
|
||||
Pair<Integer, Long> result = loadCountAndTime(parser);
|
||||
statsOut.nonInteractiveCount = result.first;
|
||||
statsOut.nonInteractiveDuration = result.second;
|
||||
} break;
|
||||
|
||||
case PACKAGE_TAG:
|
||||
loadUsageStats(parser, statsOut);
|
||||
break;
|
||||
@@ -346,6 +378,11 @@ final class UsageStatsXmlV1 {
|
||||
public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
|
||||
XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
|
||||
|
||||
writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveCount, stats.interactiveDuration);
|
||||
|
||||
writeCountAndTime(xml, NON_INTERACTIVE_TAG, stats.nonInteractiveCount,
|
||||
stats.nonInteractiveDuration);
|
||||
|
||||
xml.startTag(null, PACKAGES_TAG);
|
||||
final int statsCount = stats.packageStats.size();
|
||||
for (int i = 0; i < statsCount; i++) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.EventStats;
|
||||
import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
@@ -191,21 +192,31 @@ class UserUsageStatsService {
|
||||
}
|
||||
|
||||
for (IntervalStats stats : mCurrentStats) {
|
||||
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
|
||||
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
|
||||
} else if (event.mEventType == UsageEvents.Event.CHOOSER_ACTION) {
|
||||
stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
|
||||
String[] annotations = event.mContentAnnotations;
|
||||
if (annotations != null) {
|
||||
for (String annotation : annotations) {
|
||||
stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
|
||||
switch (event.mEventType) {
|
||||
case UsageEvents.Event.CONFIGURATION_CHANGE: {
|
||||
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
|
||||
} break;
|
||||
case UsageEvents.Event.CHOOSER_ACTION: {
|
||||
stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
|
||||
String[] annotations = event.mContentAnnotations;
|
||||
if (annotations != null) {
|
||||
for (String annotation : annotations) {
|
||||
stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
|
||||
if (incrementAppLaunch) {
|
||||
stats.incrementAppLaunchCount(event.mPackage);
|
||||
}
|
||||
} break;
|
||||
case UsageEvents.Event.SCREEN_INTERACTIVE: {
|
||||
stats.updateScreenInteractive(event.mTimeStamp);
|
||||
} break;
|
||||
case UsageEvents.Event.SCREEN_NON_INTERACTIVE: {
|
||||
stats.updateScreenNonInteractive(event.mTimeStamp);
|
||||
} break;
|
||||
default: {
|
||||
stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
|
||||
if (incrementAppLaunch) {
|
||||
stats.incrementAppLaunchCount(event.mPackage);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +257,15 @@ class UserUsageStatsService {
|
||||
}
|
||||
};
|
||||
|
||||
private static final StatCombiner<EventStats> sEventStatsCombiner =
|
||||
new StatCombiner<EventStats>() {
|
||||
@Override
|
||||
public void combine(IntervalStats stats, boolean mutable,
|
||||
List<EventStats> accResult) {
|
||||
stats.addEventStatsTo(accResult);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic query method that selects the appropriate IntervalStats for the specified time range
|
||||
* and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
|
||||
@@ -325,6 +345,10 @@ class UserUsageStatsService {
|
||||
return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
|
||||
}
|
||||
|
||||
List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
|
||||
return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
|
||||
}
|
||||
|
||||
UsageEvents queryEvents(final long beginTime, final long endTime,
|
||||
boolean obfuscateInstantApps) {
|
||||
final ArraySet<String> names = new ArraySet<>();
|
||||
@@ -448,6 +472,7 @@ class UserUsageStatsService {
|
||||
}
|
||||
|
||||
stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
|
||||
stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1);
|
||||
}
|
||||
|
||||
persistActiveStats();
|
||||
@@ -640,6 +665,18 @@ class UserUsageStatsService {
|
||||
}
|
||||
}
|
||||
|
||||
void printEventAggregation(IndentingPrintWriter pw, String label, int count, long duration,
|
||||
boolean prettyDates) {
|
||||
if (count != 0 || duration != 0) {
|
||||
pw.print(label);
|
||||
pw.print(": ");
|
||||
pw.print(count);
|
||||
pw.print("x for ");
|
||||
pw.print(formatElapsedTime(duration, prettyDates));
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
|
||||
boolean prettyDates, boolean skipEvents, String pkg) {
|
||||
if (prettyDates) {
|
||||
@@ -713,6 +750,13 @@ class UserUsageStatsService {
|
||||
pw.println();
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
pw.println("event aggregations");
|
||||
pw.increaseIndent();
|
||||
printEventAggregation(pw, "screen-interactive", stats.interactiveCount,
|
||||
stats.interactiveDuration, prettyDates);
|
||||
printEventAggregation(pw, "screen-non-interactive", stats.nonInteractiveCount,
|
||||
stats.nonInteractiveDuration, prettyDates);
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
// The last 24 hours of events is already printed in the non checkin dump
|
||||
@@ -781,6 +825,10 @@ class UserUsageStatsService {
|
||||
return "SLICE_PINNED";
|
||||
case UsageEvents.Event.SLICE_PINNED_PRIV:
|
||||
return "SLICE_PINNED_PRIV";
|
||||
case UsageEvents.Event.SCREEN_INTERACTIVE:
|
||||
return "SCREEN_INTERACTIVE";
|
||||
case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
|
||||
return "SCREEN_NON_INTERACTIVE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user