Merge "Work on issue #74404949: Screen state usage API" into pi-dev

This commit is contained in:
Dianne Hackborn
2018-03-27 22:40:51 +00:00
committed by Android (Google) Code Review
13 changed files with 497 additions and 16 deletions

View File

@@ -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);

View 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];
}
};
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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)) {

View File

@@ -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++) {

View File

@@ -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";
}