Add Configuration changes to UsageStats
Bug:17354208 Change-Id: I9b2f595e51b656607e30e798926cfb7e25134944
This commit is contained in:
@@ -5680,6 +5680,19 @@ package android.app.job {
|
||||
|
||||
package android.app.usage {
|
||||
|
||||
public final class ConfigurationStats implements android.os.Parcelable {
|
||||
ctor public ConfigurationStats(android.app.usage.ConfigurationStats);
|
||||
method public int describeContents();
|
||||
method public int getActivationCount();
|
||||
method public android.content.res.Configuration getConfiguration();
|
||||
method public long getFirstTimeStamp();
|
||||
method public long getLastTimeActive();
|
||||
method public long getLastTimeStamp();
|
||||
method public long getTotalTimeActive();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
}
|
||||
|
||||
public final class UsageEvents implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
|
||||
@@ -5692,9 +5705,11 @@ package android.app.usage {
|
||||
public static final class UsageEvents.Event {
|
||||
ctor public UsageEvents.Event();
|
||||
method public java.lang.String getClassName();
|
||||
method public android.content.res.Configuration getConfiguration();
|
||||
method public int getEventType();
|
||||
method public java.lang.String getPackageName();
|
||||
method public long getTimeStamp();
|
||||
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
|
||||
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
|
||||
@@ -5715,6 +5730,7 @@ package android.app.usage {
|
||||
|
||||
public final class UsageStatsManager {
|
||||
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 android.app.usage.UsageEvents queryEvents(long, long);
|
||||
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
|
||||
field public static final int INTERVAL_BEST = 4; // 0x4
|
||||
|
||||
161
core/java/android/app/usage/ConfigurationStats.java
Normal file
161
core/java/android/app/usage/ConfigurationStats.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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 android.content.res.Configuration;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Represents the usage statistics of a device {@link android.content.res.Configuration} for a
|
||||
* specific time range.
|
||||
*/
|
||||
public final class ConfigurationStats implements Parcelable {
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public Configuration mConfiguration;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mBeginTimeStamp;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mEndTimeStamp;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mLastTimeActive;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public long mTotalTimeActive;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public int mActivationCount;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public ConfigurationStats() {
|
||||
}
|
||||
|
||||
public ConfigurationStats(ConfigurationStats stats) {
|
||||
mConfiguration = stats.mConfiguration;
|
||||
mBeginTimeStamp = stats.mBeginTimeStamp;
|
||||
mEndTimeStamp = stats.mEndTimeStamp;
|
||||
mLastTimeActive = stats.mLastTimeActive;
|
||||
mTotalTimeActive = stats.mTotalTimeActive;
|
||||
mActivationCount = stats.mActivationCount;
|
||||
}
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return mConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the beginning of the time range this {@link ConfigurationStats} 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 ConfigurationStats} represents,
|
||||
* measured in milliseconds since the epoch.
|
||||
* <p/>
|
||||
* See {@link System#currentTimeMillis()}.
|
||||
*/
|
||||
public long getLastTimeStamp() {
|
||||
return mEndTimeStamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time this configuration was active, measured in milliseconds since the epoch.
|
||||
* <p/>
|
||||
* See {@link System#currentTimeMillis()}.
|
||||
*/
|
||||
public long getLastTimeActive() {
|
||||
return mLastTimeActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total time this configuration was active, measured in milliseconds.
|
||||
*/
|
||||
public long getTotalTimeActive() {
|
||||
return mTotalTimeActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of times this configuration was active.
|
||||
*/
|
||||
public int getActivationCount() {
|
||||
return mActivationCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
if (mConfiguration != null) {
|
||||
dest.writeInt(1);
|
||||
mConfiguration.writeToParcel(dest, flags);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
|
||||
dest.writeLong(mBeginTimeStamp);
|
||||
dest.writeLong(mEndTimeStamp);
|
||||
dest.writeLong(mLastTimeActive);
|
||||
dest.writeLong(mTotalTimeActive);
|
||||
dest.writeInt(mActivationCount);
|
||||
}
|
||||
|
||||
public static final Creator<ConfigurationStats> CREATOR = new Creator<ConfigurationStats>() {
|
||||
@Override
|
||||
public ConfigurationStats createFromParcel(Parcel source) {
|
||||
ConfigurationStats stats = new ConfigurationStats();
|
||||
if (source.readInt() != 0) {
|
||||
stats.mConfiguration = Configuration.CREATOR.createFromParcel(source);
|
||||
}
|
||||
stats.mBeginTimeStamp = source.readLong();
|
||||
stats.mEndTimeStamp = source.readLong();
|
||||
stats.mLastTimeActive = source.readLong();
|
||||
stats.mTotalTimeActive = source.readLong();
|
||||
stats.mActivationCount = source.readInt();
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationStats[] newArray(int size) {
|
||||
return new ConfigurationStats[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -27,5 +27,7 @@ import android.content.pm.ParceledListSlice;
|
||||
interface IUsageStatsManager {
|
||||
ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
|
||||
String callingPackage);
|
||||
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
|
||||
String callingPackage);
|
||||
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package android.app.usage;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
@@ -62,6 +63,11 @@ public final class UsageEvents implements Parcelable {
|
||||
*/
|
||||
public static final int CONTINUE_PREVIOUS_DAY = 4;
|
||||
|
||||
/**
|
||||
* An event type denoting that the device configuration has changed.
|
||||
*/
|
||||
public static final int CONFIGURATION_CHANGE = 5;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
@@ -82,6 +88,12 @@ public final class UsageEvents implements Parcelable {
|
||||
*/
|
||||
public int mEventType;
|
||||
|
||||
/**
|
||||
* Only present for {@link #CONFIGURATION_CHANGE} event types.
|
||||
* {@hide}
|
||||
*/
|
||||
public Configuration mConfiguration;
|
||||
|
||||
/**
|
||||
* TODO(adamlesinski): Removed before release.
|
||||
* {@hide}
|
||||
@@ -123,6 +135,14 @@ public final class UsageEvents implements Parcelable {
|
||||
public int getEventType() {
|
||||
return mEventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Configuration} for this event if the event is of type
|
||||
* {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
return mConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
// Only used when creating the resulting events. Not used for reading/unparceling.
|
||||
@@ -201,23 +221,9 @@ public final class UsageEvents implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int packageIndex = mParcel.readInt();
|
||||
if (packageIndex >= 0) {
|
||||
eventOut.mPackage = mStringPool[packageIndex];
|
||||
} else {
|
||||
eventOut.mPackage = null;
|
||||
}
|
||||
readEventFromParcel(mParcel, eventOut);
|
||||
|
||||
final int classIndex = mParcel.readInt();
|
||||
if (classIndex >= 0) {
|
||||
eventOut.mClass = mStringPool[classIndex];
|
||||
} else {
|
||||
eventOut.mClass = null;
|
||||
}
|
||||
eventOut.mEventType = mParcel.readInt();
|
||||
eventOut.mTimeStamp = mParcel.readLong();
|
||||
mIndex++;
|
||||
|
||||
if (mIndex >= mEventCount) {
|
||||
mParcel.recycle();
|
||||
mParcel = null;
|
||||
@@ -235,11 +241,6 @@ public final class UsageEvents implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int findStringIndex(String str) {
|
||||
final int index = Arrays.binarySearch(mStringPool, str);
|
||||
if (index < 0) {
|
||||
@@ -248,6 +249,66 @@ public final class UsageEvents implements Parcelable {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single event to the parcel. Modify this when updating {@link Event}.
|
||||
*/
|
||||
private void writeEventToParcel(Event event, Parcel p, int flags) {
|
||||
final int packageIndex;
|
||||
if (event.mPackage != null) {
|
||||
packageIndex = findStringIndex(event.mPackage);
|
||||
} else {
|
||||
packageIndex = -1;
|
||||
}
|
||||
|
||||
final int classIndex;
|
||||
if (event.mClass != null) {
|
||||
classIndex = findStringIndex(event.mClass);
|
||||
} else {
|
||||
classIndex = -1;
|
||||
}
|
||||
p.writeInt(packageIndex);
|
||||
p.writeInt(classIndex);
|
||||
p.writeInt(event.mEventType);
|
||||
p.writeLong(event.mTimeStamp);
|
||||
|
||||
if (event.mEventType == Event.CONFIGURATION_CHANGE) {
|
||||
event.mConfiguration.writeToParcel(p, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single event from the parcel. Modify this when updating {@link Event}.
|
||||
*/
|
||||
private void readEventFromParcel(Parcel p, Event eventOut) {
|
||||
final int packageIndex = p.readInt();
|
||||
if (packageIndex >= 0) {
|
||||
eventOut.mPackage = mStringPool[packageIndex];
|
||||
} else {
|
||||
eventOut.mPackage = null;
|
||||
}
|
||||
|
||||
final int classIndex = p.readInt();
|
||||
if (classIndex >= 0) {
|
||||
eventOut.mClass = mStringPool[classIndex];
|
||||
} else {
|
||||
eventOut.mClass = null;
|
||||
}
|
||||
eventOut.mEventType = p.readInt();
|
||||
eventOut.mTimeStamp = p.readLong();
|
||||
|
||||
// Extract the configuration for configuration change events.
|
||||
if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) {
|
||||
eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
|
||||
} else {
|
||||
eventOut.mConfiguration = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mEventCount);
|
||||
@@ -262,25 +323,9 @@ public final class UsageEvents implements Parcelable {
|
||||
p.setDataPosition(0);
|
||||
for (int i = 0; i < mEventCount; i++) {
|
||||
final Event event = mEventsToWrite.get(i);
|
||||
|
||||
final int packageIndex;
|
||||
if (event.mPackage != null) {
|
||||
packageIndex = findStringIndex(event.mPackage);
|
||||
} else {
|
||||
packageIndex = -1;
|
||||
}
|
||||
|
||||
final int classIndex;
|
||||
if (event.mClass != null) {
|
||||
classIndex = findStringIndex(event.mClass);
|
||||
} else {
|
||||
classIndex = -1;
|
||||
}
|
||||
p.writeInt(packageIndex);
|
||||
p.writeInt(classIndex);
|
||||
p.writeInt(event.getEventType());
|
||||
p.writeLong(event.getTimeStamp());
|
||||
writeEventToParcel(event, p, flags);
|
||||
}
|
||||
|
||||
final int listByteLength = p.dataPosition();
|
||||
|
||||
// Write the total length of the data.
|
||||
|
||||
@@ -125,9 +125,9 @@ public final class UsageStatsManager {
|
||||
* @see #INTERVAL_YEARLY
|
||||
* @see #INTERVAL_BEST
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
|
||||
endTime, mContext.getOpPackageName());
|
||||
if (slice != null) {
|
||||
@@ -136,7 +136,32 @@ public final class UsageStatsManager {
|
||||
} catch (RemoteException e) {
|
||||
// fallthrough and return null.
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hardware configurations the device was in for the given time range, aggregated by
|
||||
* the specified interval. The results are ordered as in
|
||||
* {@link #queryUsageStats(int, long, long)}.
|
||||
*
|
||||
* @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 ConfigurationStats} or null if none are available.
|
||||
*/
|
||||
public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime,
|
||||
long endTime) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
ParceledListSlice<ConfigurationStats> slice = mService.queryConfigurationStats(
|
||||
intervalType, beginTime, endTime, mContext.getOpPackageName());
|
||||
if (slice != null) {
|
||||
return slice.getList();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// fallthrough and return the empty list.
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.app.usage;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
/**
|
||||
* UsageStatsManager local system service interface.
|
||||
@@ -28,14 +29,19 @@ public abstract class UsageStatsManagerInternal {
|
||||
/**
|
||||
* Reports an event to the UsageStatsManager.
|
||||
*
|
||||
* @param component The component for which this event ocurred.
|
||||
* @param component The component for which this event occurred.
|
||||
* @param userId The user id to which the component belongs to.
|
||||
* @param timeStamp The time at which this event ocurred.
|
||||
* @param eventType The event that occured. Valid values can be found at
|
||||
* @param eventType The event that occurred. Valid values can be found at
|
||||
* {@link UsageEvents}
|
||||
*/
|
||||
public abstract void reportEvent(ComponentName component, int userId,
|
||||
long timeStamp, int eventType);
|
||||
public abstract void reportEvent(ComponentName component, int userId, int eventType);
|
||||
|
||||
/**
|
||||
* Reports a configuration change to the UsageStatsManager.
|
||||
*
|
||||
* @param config The new device configuration.
|
||||
*/
|
||||
public abstract void reportConfigurationChange(Configuration config, int userId);
|
||||
|
||||
/**
|
||||
* Prepares the UsageStatsService for shutdown.
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package android.content.res;
|
||||
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
@@ -23,7 +29,7 @@ import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import java.text.Format;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -1353,8 +1359,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration
|
||||
* Returns a string representation of the configuration that can be parsed
|
||||
* by build tools (like AAPT).
|
||||
*
|
||||
*
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static String resourceQualifierString(Configuration config) {
|
||||
@@ -1568,4 +1572,229 @@ public final class Configuration implements Parcelable, Comparable<Configuration
|
||||
parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
|
||||
return TextUtils.join("-", parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a delta Configuration between <code>base</code> and <code>change</code>. The
|
||||
* resulting delta can be used with {@link #updateFrom(Configuration)}.
|
||||
* <p />
|
||||
* Caveat: If the any of the Configuration's members becomes undefined, then
|
||||
* {@link #updateFrom(Configuration)} will treat it as a no-op and not update that member.
|
||||
*
|
||||
* This is fine for device configurations as no member is ever undefined.
|
||||
* {@hide}
|
||||
*/
|
||||
public static Configuration generateDelta(Configuration base, Configuration change) {
|
||||
final Configuration delta = new Configuration();
|
||||
if (base.fontScale != change.fontScale) {
|
||||
delta.fontScale = change.fontScale;
|
||||
}
|
||||
|
||||
if (base.mcc != change.mcc) {
|
||||
delta.mcc = change.mcc;
|
||||
}
|
||||
|
||||
if (base.mnc != change.mnc) {
|
||||
delta.mnc = change.mnc;
|
||||
}
|
||||
|
||||
if ((base.locale == null && change.locale != null) ||
|
||||
(base.locale != null && !base.locale.equals(change.locale))) {
|
||||
delta.locale = change.locale;
|
||||
}
|
||||
|
||||
if (base.touchscreen != change.touchscreen) {
|
||||
delta.touchscreen = change.touchscreen;
|
||||
}
|
||||
|
||||
if (base.keyboard != change.keyboard) {
|
||||
delta.keyboard = change.keyboard;
|
||||
}
|
||||
|
||||
if (base.keyboardHidden != change.keyboardHidden) {
|
||||
delta.keyboardHidden = change.keyboardHidden;
|
||||
}
|
||||
|
||||
if (base.navigation != change.navigation) {
|
||||
delta.navigation = change.navigation;
|
||||
}
|
||||
|
||||
if (base.navigationHidden != change.navigationHidden) {
|
||||
delta.navigationHidden = change.navigationHidden;
|
||||
}
|
||||
|
||||
if (base.orientation != change.orientation) {
|
||||
delta.orientation = change.orientation;
|
||||
}
|
||||
|
||||
if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) !=
|
||||
(change.screenLayout & SCREENLAYOUT_SIZE_MASK)) {
|
||||
delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK;
|
||||
}
|
||||
|
||||
if ((base.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK) !=
|
||||
(change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
|
||||
delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
|
||||
}
|
||||
|
||||
if ((base.screenLayout & SCREENLAYOUT_LONG_MASK) !=
|
||||
(change.screenLayout & SCREENLAYOUT_LONG_MASK)) {
|
||||
delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK;
|
||||
}
|
||||
|
||||
if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
|
||||
delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
|
||||
}
|
||||
|
||||
if ((base.uiMode & UI_MODE_NIGHT_MASK) != (change.uiMode & UI_MODE_NIGHT_MASK)) {
|
||||
delta.uiMode |= change.uiMode & UI_MODE_NIGHT_MASK;
|
||||
}
|
||||
|
||||
if (base.screenWidthDp != change.screenWidthDp) {
|
||||
delta.screenWidthDp = change.screenWidthDp;
|
||||
}
|
||||
|
||||
if (base.screenHeightDp != change.screenHeightDp) {
|
||||
delta.screenHeightDp = change.screenHeightDp;
|
||||
}
|
||||
|
||||
if (base.smallestScreenWidthDp != change.smallestScreenWidthDp) {
|
||||
delta.smallestScreenWidthDp = change.smallestScreenWidthDp;
|
||||
}
|
||||
|
||||
if (base.densityDpi != change.densityDpi) {
|
||||
delta.densityDpi = change.densityDpi;
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
private static final String XML_ATTR_FONT_SCALE = "fs";
|
||||
private static final String XML_ATTR_MCC = "mcc";
|
||||
private static final String XML_ATTR_MNC = "mnc";
|
||||
private static final String XML_ATTR_LOCALE = "locale";
|
||||
private static final String XML_ATTR_TOUCHSCREEN = "touch";
|
||||
private static final String XML_ATTR_KEYBOARD = "key";
|
||||
private static final String XML_ATTR_KEYBOARD_HIDDEN = "keyHid";
|
||||
private static final String XML_ATTR_HARD_KEYBOARD_HIDDEN = "hardKeyHid";
|
||||
private static final String XML_ATTR_NAVIGATION = "nav";
|
||||
private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
|
||||
private static final String XML_ATTR_ORIENTATION = "ori";
|
||||
private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
|
||||
private static final String XML_ATTR_UI_MODE = "ui";
|
||||
private static final String XML_ATTR_SCREEN_WIDTH = "width";
|
||||
private static final String XML_ATTR_SCREEN_HEIGHT = "height";
|
||||
private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
|
||||
private static final String XML_ATTR_DENSITY = "density";
|
||||
|
||||
/**
|
||||
* Reads the attributes corresponding to Configuration member fields from the Xml parser.
|
||||
* The parser is expected to be on a tag which has Configuration attributes.
|
||||
*
|
||||
* @param parser The Xml parser from which to read attributes.
|
||||
* @param configOut The Configuration to populate from the Xml attributes.
|
||||
* {@hide}
|
||||
*/
|
||||
public static void readXmlAttrs(XmlPullParser parser, Configuration configOut)
|
||||
throws XmlPullParserException, IOException {
|
||||
configOut.fontScale = Float.intBitsToFloat(
|
||||
XmlUtils.readIntAttribute(parser, XML_ATTR_FONT_SCALE, 0));
|
||||
configOut.mcc = XmlUtils.readIntAttribute(parser, XML_ATTR_MCC, 0);
|
||||
configOut.mnc = XmlUtils.readIntAttribute(parser, XML_ATTR_MNC, 0);
|
||||
|
||||
final String localeStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALE);
|
||||
if (localeStr != null) {
|
||||
configOut.locale = Locale.forLanguageTag(localeStr);
|
||||
}
|
||||
|
||||
configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
|
||||
TOUCHSCREEN_UNDEFINED);
|
||||
configOut.keyboard = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD,
|
||||
KEYBOARD_UNDEFINED);
|
||||
configOut.keyboardHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD_HIDDEN,
|
||||
KEYBOARDHIDDEN_UNDEFINED);
|
||||
configOut.hardKeyboardHidden =
|
||||
XmlUtils.readIntAttribute(parser, XML_ATTR_HARD_KEYBOARD_HIDDEN,
|
||||
HARDKEYBOARDHIDDEN_UNDEFINED);
|
||||
configOut.navigation = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION,
|
||||
NAVIGATION_UNDEFINED);
|
||||
configOut.navigationHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION_HIDDEN,
|
||||
NAVIGATIONHIDDEN_UNDEFINED);
|
||||
configOut.orientation = XmlUtils.readIntAttribute(parser, XML_ATTR_ORIENTATION,
|
||||
ORIENTATION_UNDEFINED);
|
||||
configOut.screenLayout = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_LAYOUT,
|
||||
SCREENLAYOUT_UNDEFINED);
|
||||
configOut.uiMode = XmlUtils.readIntAttribute(parser, XML_ATTR_UI_MODE, 0);
|
||||
configOut.screenWidthDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_WIDTH,
|
||||
SCREEN_WIDTH_DP_UNDEFINED);
|
||||
configOut.screenHeightDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_HEIGHT,
|
||||
SCREEN_HEIGHT_DP_UNDEFINED);
|
||||
configOut.smallestScreenWidthDp =
|
||||
XmlUtils.readIntAttribute(parser, XML_ATTR_SMALLEST_WIDTH,
|
||||
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
|
||||
configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
|
||||
DENSITY_DPI_UNDEFINED);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the Configuration's member fields as attributes into the XmlSerializer.
|
||||
* The serializer is expected to have already started a tag so that attributes can be
|
||||
* immediately written.
|
||||
*
|
||||
* @param xml The serializer to which to write the attributes.
|
||||
* @param config The Configuration whose member fields to write.
|
||||
* {@hide}
|
||||
*/
|
||||
public static void writeXmlAttrs(XmlSerializer xml, Configuration config) throws IOException {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_FONT_SCALE,
|
||||
Float.floatToIntBits(config.fontScale));
|
||||
if (config.mcc != 0) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_MCC, config.mcc);
|
||||
}
|
||||
if (config.mnc != 0) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc);
|
||||
}
|
||||
if (config.locale != null) {
|
||||
XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALE, config.locale.toLanguageTag());
|
||||
}
|
||||
if (config.touchscreen != TOUCHSCREEN_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen);
|
||||
}
|
||||
if (config.keyboard != KEYBOARD_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD, config.keyboard);
|
||||
}
|
||||
if (config.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD_HIDDEN, config.keyboardHidden);
|
||||
}
|
||||
if (config.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_HARD_KEYBOARD_HIDDEN,
|
||||
config.hardKeyboardHidden);
|
||||
}
|
||||
if (config.navigation != NAVIGATION_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION, config.navigation);
|
||||
}
|
||||
if (config.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION_HIDDEN, config.navigationHidden);
|
||||
}
|
||||
if (config.orientation != ORIENTATION_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_ORIENTATION, config.orientation);
|
||||
}
|
||||
if (config.screenLayout != SCREENLAYOUT_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout);
|
||||
}
|
||||
if (config.uiMode != 0) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode);
|
||||
}
|
||||
if (config.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_WIDTH, config.screenWidthDp);
|
||||
}
|
||||
if (config.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_HEIGHT, config.screenHeightDp);
|
||||
}
|
||||
if (config.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_SMALLEST_WIDTH, config.smallestScreenWidthDp);
|
||||
}
|
||||
if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
|
||||
XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3209,7 +3209,6 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
if (resumed) {
|
||||
if (mUsageStatsService != null) {
|
||||
mUsageStatsService.reportEvent(component.realActivity, component.userId,
|
||||
System.currentTimeMillis(),
|
||||
UsageEvents.Event.MOVE_TO_FOREGROUND);
|
||||
}
|
||||
synchronized (stats) {
|
||||
@@ -3218,7 +3217,6 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
} else {
|
||||
if (mUsageStatsService != null) {
|
||||
mUsageStatsService.reportEvent(component.realActivity, component.userId,
|
||||
System.currentTimeMillis(),
|
||||
UsageEvents.Event.MOVE_TO_BACKGROUND);
|
||||
}
|
||||
synchronized (stats) {
|
||||
@@ -15918,6 +15916,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
newConfig.seq = mConfigurationSeq;
|
||||
mConfiguration = newConfig;
|
||||
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
|
||||
mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
|
||||
//mUsageStatsService.noteStartConfig(newConfig);
|
||||
|
||||
final Configuration configCopy = new Configuration(mConfiguration);
|
||||
|
||||
@@ -15,17 +15,23 @@
|
||||
*/
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class IntervalStats {
|
||||
public long beginTime;
|
||||
public long endTime;
|
||||
public long lastTimeSaved;
|
||||
public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
|
||||
public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
|
||||
public Configuration activeConfiguration;
|
||||
public TimeSparseArray<UsageEvents.Event> events;
|
||||
|
||||
// A string cache. This is important as when we're parsing XML files, we don't want to
|
||||
@@ -34,18 +40,49 @@ class IntervalStats {
|
||||
// strings that had identical copies in the cache.
|
||||
private final ArraySet<String> mStringCache = new ArraySet<>();
|
||||
|
||||
/**
|
||||
* Gets the UsageStats object for the given package, or creates one and adds it internally.
|
||||
*/
|
||||
UsageStats getOrCreateUsageStats(String packageName) {
|
||||
UsageStats usageStats = stats.get(packageName);
|
||||
if (usageStats == null) {
|
||||
usageStats = new UsageStats();
|
||||
usageStats.mPackageName = packageName;
|
||||
usageStats.mPackageName = getCachedStringRef(packageName);
|
||||
usageStats.mBeginTimeStamp = beginTime;
|
||||
usageStats.mEndTimeStamp = endTime;
|
||||
stats.put(packageName, usageStats);
|
||||
stats.put(usageStats.mPackageName, usageStats);
|
||||
}
|
||||
return usageStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ConfigurationStats object for the given configuration, or creates one and adds it
|
||||
* internally.
|
||||
*/
|
||||
ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
|
||||
ConfigurationStats configStats = configurations.get(config);
|
||||
if (configStats == null) {
|
||||
configStats = new ConfigurationStats();
|
||||
configStats.mBeginTimeStamp = beginTime;
|
||||
configStats.mEndTimeStamp = endTime;
|
||||
configStats.mConfiguration = config;
|
||||
configurations.put(config, configStats);
|
||||
}
|
||||
return configStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a UsageEvents.Event, but does not add it internally.
|
||||
*/
|
||||
UsageEvents.Event buildEvent(String packageName, String className) {
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
event.mPackage = getCachedStringRef(packageName);
|
||||
if (className != null) {
|
||||
event.mClass = getCachedStringRef(className);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
void update(String packageName, long timeStamp, int eventType) {
|
||||
UsageStats usageStats = getOrCreateUsageStats(packageName);
|
||||
|
||||
@@ -61,6 +98,28 @@ class IntervalStats {
|
||||
usageStats.mLastEvent = eventType;
|
||||
usageStats.mLastTimeUsed = timeStamp;
|
||||
usageStats.mEndTimeStamp = timeStamp;
|
||||
|
||||
if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
|
||||
usageStats.mLaunchCount += 1;
|
||||
}
|
||||
|
||||
endTime = timeStamp;
|
||||
}
|
||||
|
||||
void updateConfigurationStats(Configuration config, long timeStamp) {
|
||||
if (activeConfiguration != null) {
|
||||
ConfigurationStats activeStats = configurations.get(activeConfiguration);
|
||||
activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
|
||||
activeStats.mLastTimeActive = timeStamp - 1;
|
||||
}
|
||||
|
||||
if (config != null) {
|
||||
ConfigurationStats configStats = getOrCreateConfigurationStats(config);
|
||||
configStats.mLastTimeActive = timeStamp;
|
||||
configStats.mActivationCount += 1;
|
||||
activeConfiguration = configStats.mConfiguration;
|
||||
}
|
||||
|
||||
endTime = timeStamp;
|
||||
}
|
||||
|
||||
@@ -72,13 +131,4 @@ class IntervalStats {
|
||||
}
|
||||
return mStringCache.valueAt(index);
|
||||
}
|
||||
|
||||
UsageEvents.Event buildEvent(String packageName, String className) {
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
event.mPackage = getCachedStringRef(packageName);
|
||||
if (className != null) {
|
||||
event.mClass = getCachedStringRef(className);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Slog;
|
||||
@@ -133,9 +132,30 @@ class UsageStatsDatabase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all {@link UsageStats} for the given range and interval type.
|
||||
* Figures out what to extract from the given IntervalStats object.
|
||||
*/
|
||||
public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
|
||||
interface StatCombiner<T> {
|
||||
|
||||
/**
|
||||
* Implementations should extract interesting from <code>stats</code> and add it
|
||||
* to the <code>accumulatedResult</code> list.
|
||||
*
|
||||
* If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
|
||||
* which means you should make a copy of the data before adding it to the
|
||||
* <code>accumulatedResult</code> list.
|
||||
*
|
||||
* @param stats The {@link IntervalStats} object selected.
|
||||
* @param mutable Whether or not the data inside the stats object is mutable.
|
||||
* @param accumulatedResult The list to which to add extracted data.
|
||||
*/
|
||||
void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all {@link IntervalStats} for the given range and interval type.
|
||||
*/
|
||||
public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
|
||||
StatCombiner<T> combiner) {
|
||||
synchronized (mLock) {
|
||||
if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
|
||||
throw new IllegalArgumentException("Bad interval type " + intervalType);
|
||||
@@ -157,7 +177,7 @@ class UsageStatsDatabase {
|
||||
|
||||
try {
|
||||
IntervalStats stats = new IntervalStats();
|
||||
ArrayList<UsageStats> results = new ArrayList<>();
|
||||
ArrayList<T> results = new ArrayList<>();
|
||||
for (int i = startIndex; i <= endIndex; i++) {
|
||||
final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
|
||||
|
||||
@@ -167,7 +187,7 @@ class UsageStatsDatabase {
|
||||
|
||||
UsageStatsXml.read(f, stats);
|
||||
if (beginTime < stats.endTime) {
|
||||
results.addAll(stats.stats.values());
|
||||
combiner.combine(stats, false, results);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
@@ -209,6 +229,10 @@ class UsageStatsDatabase {
|
||||
public void prune() {
|
||||
synchronized (mLock) {
|
||||
long timeNow = System.currentTimeMillis();
|
||||
mCal.setTimeInMillis(timeNow);
|
||||
mCal.add(Calendar.YEAR, -3);
|
||||
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
|
||||
mCal.getTimeInMillis());
|
||||
|
||||
mCal.setTimeInMillis(timeNow);
|
||||
mCal.add(Calendar.MONTH, -6);
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.usage;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.IUsageStatsManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
@@ -30,11 +31,13 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Binder;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.ArraySet;
|
||||
@@ -218,8 +221,7 @@ public class UsageStatsService extends SystemService implements
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) {
|
||||
final long timeNow = System.currentTimeMillis();
|
||||
if (beginTime > timeNow) {
|
||||
if (!validRange(beginTime, endTime)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -229,18 +231,26 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
|
||||
long endTime) {
|
||||
if (!validRange(beginTime, endTime)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId);
|
||||
return service.queryConfigurationStats(bucketType, beginTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
UsageEvents queryEvents(int userId, long beginTime, long endTime) {
|
||||
final long timeNow = System.currentTimeMillis();
|
||||
|
||||
// Adjust the endTime so that we don't query for the latest events.
|
||||
// This is to prevent apps from making decision based on what app launched them,
|
||||
// etc.
|
||||
endTime = Math.min(endTime, timeNow - END_TIME_DELAY);
|
||||
|
||||
if (beginTime > endTime) {
|
||||
if (!validRange(beginTime, endTime)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -250,6 +260,11 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean validRange(long beginTime, long endTime) {
|
||||
final long timeNow = System.currentTimeMillis();
|
||||
return beginTime <= timeNow && beginTime < endTime;
|
||||
}
|
||||
|
||||
private void flushToDiskLocked() {
|
||||
final int userCount = mUserState.size();
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
@@ -322,6 +337,28 @@ public class UsageStatsService extends SystemService implements
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParceledListSlice<ConfigurationStats> queryConfigurationStats(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<ConfigurationStats> results =
|
||||
UsageStatsService.this.queryConfigurationStats(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)) {
|
||||
@@ -346,8 +383,7 @@ public class UsageStatsService extends SystemService implements
|
||||
private class LocalService extends UsageStatsManagerInternal {
|
||||
|
||||
@Override
|
||||
public void reportEvent(ComponentName component, int userId,
|
||||
long timeStamp, int eventType) {
|
||||
public void reportEvent(ComponentName component, int userId, int eventType) {
|
||||
if (component == null) {
|
||||
Slog.w(TAG, "Event reported without a component name");
|
||||
return;
|
||||
@@ -356,11 +392,26 @@ public class UsageStatsService extends SystemService implements
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
event.mPackage = component.getPackageName();
|
||||
event.mClass = component.getClassName();
|
||||
event.mTimeStamp = timeStamp;
|
||||
event.mTimeStamp = System.currentTimeMillis();
|
||||
event.mEventType = eventType;
|
||||
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportConfigurationChange(Configuration config, int userId) {
|
||||
if (config == null) {
|
||||
Slog.w(TAG, "Configuration event reported with a null config");
|
||||
return;
|
||||
}
|
||||
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
event.mPackage = "android";
|
||||
event.mTimeStamp = System.currentTimeMillis();
|
||||
event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
|
||||
event.mConfiguration = new Configuration(config);
|
||||
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareShutdown() {
|
||||
// This method *WILL* do IO work, but we must block until it is finished or else
|
||||
|
||||
@@ -20,68 +20,69 @@ import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* UsageStats reader/writer for version 1 of the XML format.
|
||||
*/
|
||||
final class UsageStatsXmlV1 {
|
||||
private static final String PACKAGE_TAG = "package";
|
||||
private static final String CONFIGURATION_TAG = "config";
|
||||
private static final String EVENT_LOG_TAG = "event-log";
|
||||
|
||||
private static final String BEGIN_TIME_ATTR = "beginTime";
|
||||
private static final String END_TIME_ATTR = "endTime";
|
||||
private static final String PACKAGE_TAG = "package";
|
||||
private static final String NAME_ATTR = "name";
|
||||
private static final String PACKAGE_ATTR = "package";
|
||||
private static final String CLASS_ATTR = "class";
|
||||
private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
|
||||
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
|
||||
private static final String COUNT_ATTR = "count";
|
||||
private static final String ACTIVE_ATTR = "active";
|
||||
private static final String LAST_EVENT_ATTR = "lastEvent";
|
||||
private static final String EVENT_LOG_TAG = "event-log";
|
||||
private static final String TYPE_ATTR = "type";
|
||||
private static final String TIME_ATTR = "time";
|
||||
|
||||
private static UsageStats readNextUsageStats(XmlPullParser parser)
|
||||
private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
|
||||
throws XmlPullParserException, IOException {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
XmlUtils.nextElement(parser);
|
||||
}
|
||||
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG ||
|
||||
!parser.getName().equals(PACKAGE_TAG)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String name = parser.getAttributeValue(null, NAME_ATTR);
|
||||
if (name == null) {
|
||||
throw new ProtocolException("no " + NAME_ATTR + " attribute present");
|
||||
}
|
||||
|
||||
UsageStats stats = new UsageStats();
|
||||
stats.mPackageName = name;
|
||||
UsageStats stats = statsOut.getOrCreateUsageStats(name);
|
||||
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
|
||||
stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
|
||||
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
return stats;
|
||||
}
|
||||
|
||||
private static UsageEvents.Event readNextEvent(XmlPullParser parser, IntervalStats statsOut)
|
||||
private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
|
||||
throws XmlPullParserException, IOException {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
XmlUtils.nextElement(parser);
|
||||
}
|
||||
final Configuration config = new Configuration();
|
||||
Configuration.readXmlAttrs(parser, config);
|
||||
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG ||
|
||||
!parser.getName().equals(EVENT_LOG_TAG)) {
|
||||
return null;
|
||||
ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
|
||||
configStats.mLastTimeActive = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
|
||||
configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
|
||||
configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
|
||||
if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
|
||||
statsOut.activeConfiguration = configStats.mConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
|
||||
throws XmlPullParserException, IOException {
|
||||
String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
|
||||
String className;
|
||||
if (packageName == null) {
|
||||
@@ -105,31 +106,60 @@ final class UsageStatsXmlV1 {
|
||||
UsageEvents.Event event = statsOut.buildEvent(packageName, className);
|
||||
event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
|
||||
event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
return event;
|
||||
}
|
||||
|
||||
private static void writeUsageStats(FastXmlSerializer serializer, UsageStats stats)
|
||||
throws IOException {
|
||||
serializer.startTag(null, PACKAGE_TAG);
|
||||
serializer.attribute(null, NAME_ATTR, stats.mPackageName);
|
||||
serializer.attribute(null, TOTAL_TIME_ACTIVE_ATTR,
|
||||
Long.toString(stats.mTotalTimeInForeground));
|
||||
serializer.attribute(null, LAST_TIME_ACTIVE_ATTR, Long.toString(stats.mLastTimeUsed));
|
||||
serializer.attribute(null, LAST_EVENT_ATTR, Integer.toString(stats.mLastEvent));
|
||||
serializer.endTag(null, PACKAGE_TAG);
|
||||
}
|
||||
|
||||
private static void writeEvent(FastXmlSerializer serializer, UsageEvents.Event event)
|
||||
throws IOException {
|
||||
serializer.startTag(null, EVENT_LOG_TAG);
|
||||
serializer.attribute(null, PACKAGE_ATTR, event.mPackage);
|
||||
if (event.mClass != null) {
|
||||
serializer.attribute(null, CLASS_ATTR, event.mClass);
|
||||
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
|
||||
event.mConfiguration = new Configuration();
|
||||
Configuration.readXmlAttrs(parser, event.mConfiguration);
|
||||
}
|
||||
serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType()));
|
||||
serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp()));
|
||||
serializer.endTag(null, EVENT_LOG_TAG);
|
||||
|
||||
if (statsOut.events == null) {
|
||||
statsOut.events = new TimeSparseArray<>();
|
||||
}
|
||||
statsOut.events.put(event.mTimeStamp, event);
|
||||
}
|
||||
|
||||
private static void writeUsageStats(XmlSerializer xml, final UsageStats stats)
|
||||
throws IOException {
|
||||
xml.startTag(null, PACKAGE_TAG);
|
||||
XmlUtils.writeStringAttribute(xml, NAME_ATTR, stats.mPackageName);
|
||||
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeInForeground);
|
||||
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeUsed);
|
||||
XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, stats.mLastEvent);
|
||||
xml.endTag(null, PACKAGE_TAG);
|
||||
}
|
||||
|
||||
private static void writeConfigStats(XmlSerializer xml, final ConfigurationStats stats,
|
||||
boolean isActive) throws IOException {
|
||||
xml.startTag(null, CONFIGURATION_TAG);
|
||||
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeActive);
|
||||
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeActive);
|
||||
XmlUtils.writeIntAttribute(xml, COUNT_ATTR, stats.mActivationCount);
|
||||
if (isActive) {
|
||||
XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
|
||||
}
|
||||
|
||||
// Now write the attributes representing the configuration object.
|
||||
Configuration.writeXmlAttrs(xml, stats.mConfiguration);
|
||||
|
||||
xml.endTag(null, CONFIGURATION_TAG);
|
||||
}
|
||||
|
||||
private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event)
|
||||
throws IOException {
|
||||
xml.startTag(null, EVENT_LOG_TAG);
|
||||
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
|
||||
if (event.mClass != null) {
|
||||
XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
|
||||
}
|
||||
XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
|
||||
XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp);
|
||||
|
||||
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
|
||||
&& event.mConfiguration != null) {
|
||||
Configuration.writeXmlAttrs(xml, event.mConfiguration);
|
||||
}
|
||||
|
||||
xml.endTag(null, EVENT_LOG_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +172,8 @@ final class UsageStatsXmlV1 {
|
||||
public static void read(XmlPullParser parser, IntervalStats statsOut)
|
||||
throws XmlPullParserException, IOException {
|
||||
statsOut.stats.clear();
|
||||
statsOut.configurations.clear();
|
||||
statsOut.activeConfiguration = null;
|
||||
|
||||
if (statsOut.events != null) {
|
||||
statsOut.events.clear();
|
||||
@@ -149,21 +181,29 @@ final class UsageStatsXmlV1 {
|
||||
|
||||
statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
|
||||
statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
|
||||
XmlUtils.nextElement(parser);
|
||||
|
||||
UsageStats pkgStats;
|
||||
while ((pkgStats = readNextUsageStats(parser)) != null) {
|
||||
pkgStats.mBeginTimeStamp = statsOut.beginTime;
|
||||
pkgStats.mEndTimeStamp = statsOut.endTime;
|
||||
statsOut.stats.put(pkgStats.mPackageName, pkgStats);
|
||||
}
|
||||
|
||||
UsageEvents.Event event;
|
||||
while ((event = readNextEvent(parser, statsOut)) != null) {
|
||||
if (statsOut.events == null) {
|
||||
statsOut.events = new TimeSparseArray<>();
|
||||
int eventCode;
|
||||
int outerDepth = parser.getDepth();
|
||||
while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (eventCode != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String tag = parser.getName();
|
||||
switch (tag) {
|
||||
case PACKAGE_TAG:
|
||||
loadUsageStats(parser, statsOut);
|
||||
break;
|
||||
|
||||
case CONFIGURATION_TAG:
|
||||
loadConfigStats(parser, statsOut);
|
||||
break;
|
||||
|
||||
case EVENT_LOG_TAG:
|
||||
loadEvent(parser, statsOut);
|
||||
break;
|
||||
}
|
||||
statsOut.events.put(event.getTimeStamp(), event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,11 +224,15 @@ final class UsageStatsXmlV1 {
|
||||
writeUsageStats(serializer, stats.stats.valueAt(i));
|
||||
}
|
||||
|
||||
if (stats.events != null) {
|
||||
final int eventCount = stats.events.size();
|
||||
for (int i = 0; i < eventCount; i++) {
|
||||
writeEvent(serializer, stats.events.valueAt(i));
|
||||
}
|
||||
final int configCount = stats.configurations.size();
|
||||
for (int i = 0; i < configCount; i++) {
|
||||
boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
|
||||
writeConfigStats(serializer, stats.configurations.valueAt(i), active);
|
||||
}
|
||||
|
||||
final int eventCount = stats.events != null ? stats.events.size() : 0;
|
||||
for (int i = 0; i < eventCount; i++) {
|
||||
writeEvent(serializer, stats.events.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -108,35 +112,91 @@ class UserUsageStatsService {
|
||||
notifyStatsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
stat.updateConfigurationStats(null, stat.lastTimeSaved);
|
||||
}
|
||||
}
|
||||
|
||||
void reportEvent(UsageEvents.Event event) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
|
||||
+ "[" + event.getTimeStamp() + "]: "
|
||||
+ eventToString(event.getEventType()));
|
||||
+ "[" + event.mTimeStamp + "]: "
|
||||
+ eventToString(event.mEventType));
|
||||
}
|
||||
|
||||
if (event.getTimeStamp() >= mDailyExpiryDate.getTimeInMillis()) {
|
||||
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
|
||||
// Need to rollover
|
||||
rolloverStats();
|
||||
}
|
||||
|
||||
if (mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events == null) {
|
||||
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events = new TimeSparseArray<>();
|
||||
final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
|
||||
|
||||
final Configuration newFullConfig = event.mConfiguration;
|
||||
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
|
||||
currentDailyStats.activeConfiguration != null) {
|
||||
// Make the event configuration a delta.
|
||||
event.mConfiguration = Configuration.generateDelta(
|
||||
currentDailyStats.activeConfiguration, newFullConfig);
|
||||
}
|
||||
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event);
|
||||
|
||||
// Add the event to the daily list.
|
||||
if (currentDailyStats.events == null) {
|
||||
currentDailyStats.events = new TimeSparseArray<>();
|
||||
}
|
||||
currentDailyStats.events.put(event.mTimeStamp, event);
|
||||
|
||||
for (IntervalStats stats : mCurrentStats) {
|
||||
stats.update(event.mPackage, event.getTimeStamp(),
|
||||
event.getEventType());
|
||||
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
|
||||
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
|
||||
} else {
|
||||
stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
|
||||
}
|
||||
}
|
||||
|
||||
notifyStatsChanged();
|
||||
}
|
||||
|
||||
List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
|
||||
private static final StatCombiner<UsageStats> sUsageStatsCombiner =
|
||||
new StatCombiner<UsageStats>() {
|
||||
@Override
|
||||
public void combine(IntervalStats stats, boolean mutable,
|
||||
List<UsageStats> accResult) {
|
||||
if (!mutable) {
|
||||
accResult.addAll(stats.stats.values());
|
||||
return;
|
||||
}
|
||||
|
||||
final int statCount = stats.stats.size();
|
||||
for (int i = 0; i < statCount; i++) {
|
||||
accResult.add(new UsageStats(stats.stats.valueAt(i)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
|
||||
new StatCombiner<ConfigurationStats>() {
|
||||
@Override
|
||||
public void combine(IntervalStats stats, boolean mutable,
|
||||
List<ConfigurationStats> accResult) {
|
||||
if (!mutable) {
|
||||
accResult.addAll(stats.configurations.values());
|
||||
return;
|
||||
}
|
||||
|
||||
final int configCount = stats.configurations.size();
|
||||
for (int i = 0; i < configCount; i++) {
|
||||
accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* provided to select the stats to use from the IntervalStats object.
|
||||
*/
|
||||
private <T> List<T> queryStats(int bucketType, long beginTime, long endTime,
|
||||
StatCombiner<T> combiner) {
|
||||
if (bucketType == UsageStatsManager.INTERVAL_BEST) {
|
||||
bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
|
||||
}
|
||||
@@ -161,11 +221,8 @@ class UserUsageStatsService {
|
||||
Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
|
||||
}
|
||||
// Fast path for retrieving in-memory state.
|
||||
ArrayList<UsageStats> results = new ArrayList<>();
|
||||
final int packageCount = mCurrentStats[bucketType].stats.size();
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
results.add(new UsageStats(mCurrentStats[bucketType].stats.valueAt(i)));
|
||||
}
|
||||
ArrayList<T> results = new ArrayList<>();
|
||||
combiner.combine(mCurrentStats[bucketType], true, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -178,13 +235,21 @@ class UserUsageStatsService {
|
||||
+ beginTime + " AND endTime < " + endTime);
|
||||
}
|
||||
|
||||
final List<UsageStats> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime);
|
||||
final List<T> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime, combiner);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
|
||||
return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
|
||||
}
|
||||
|
||||
List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
|
||||
return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
|
||||
}
|
||||
|
||||
UsageEvents queryEvents(long beginTime, long endTime) {
|
||||
if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
|
||||
if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
|
||||
@@ -245,6 +310,8 @@ class UserUsageStatsService {
|
||||
|
||||
// Finish any ongoing events with an END_OF_DAY event. Make a note of which components
|
||||
// need a new CONTINUE_PREVIOUS_DAY entry.
|
||||
final Configuration previousConfig =
|
||||
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
|
||||
ArraySet<String> continuePreviousDay = new ArraySet<>();
|
||||
for (IntervalStats stat : mCurrentStats) {
|
||||
final int pkgCount = stat.stats.size();
|
||||
@@ -253,11 +320,13 @@ class UserUsageStatsService {
|
||||
if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
|
||||
pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
|
||||
continuePreviousDay.add(pkgStats.mPackageName);
|
||||
stat.update(pkgStats.mPackageName,
|
||||
mDailyExpiryDate.getTimeInMillis() - 1, UsageEvents.Event.END_OF_DAY);
|
||||
stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
|
||||
UsageEvents.Event.END_OF_DAY);
|
||||
mStatsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
|
||||
}
|
||||
|
||||
persistActiveStats();
|
||||
@@ -267,9 +336,10 @@ class UserUsageStatsService {
|
||||
final int continueCount = continuePreviousDay.size();
|
||||
for (int i = 0; i < continueCount; i++) {
|
||||
String name = continuePreviousDay.valueAt(i);
|
||||
final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
|
||||
for (IntervalStats stat : mCurrentStats) {
|
||||
stat.update(name, mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime,
|
||||
UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
|
||||
stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
|
||||
stat.updateConfigurationStats(previousConfig, beginTime);
|
||||
mStatsChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -353,6 +423,8 @@ class UserUsageStatsService {
|
||||
return "END_OF_DAY";
|
||||
case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
|
||||
return "CONTINUE_PREVIOUS_DAY";
|
||||
case UsageEvents.Event.CONFIGURATION_CHANGE:
|
||||
return "CONFIGURATION_CHANGE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ LOCAL_MODULE_TAGS := tests
|
||||
# Only compile source java files in this apk.
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
|
||||
|
||||
LOCAL_PACKAGE_NAME := UsageStatsTest
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
7
tests/UsageStatsTest/res/layout/config_row_item.xml
Normal file
7
tests/UsageStatsTest/res/layout/config_row_item.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"/>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="16dp"
|
||||
@@ -8,13 +9,10 @@
|
||||
<TextView android:id="@android:id/text1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_weight="1"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView android:id="@android:id/text2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignBaseline="@android:id/text1"/>
|
||||
</RelativeLayout>
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.util.CircularArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -35,11 +36,14 @@ public class UsageLogActivity extends ListActivity implements Runnable {
|
||||
private UsageStatsManager mUsageStatsManager;
|
||||
private Adapter mAdapter;
|
||||
private Handler mHandler = new Handler();
|
||||
private long mLastTime;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
mLastTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
|
||||
|
||||
mAdapter = new Adapter();
|
||||
setListAdapter(mAdapter);
|
||||
}
|
||||
@@ -59,24 +63,31 @@ public class UsageLogActivity extends ListActivity implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
long now = System.currentTimeMillis();
|
||||
long beginTime = now - USAGE_STATS_PERIOD;
|
||||
UsageEvents events = mUsageStatsManager.queryEvents(beginTime, now);
|
||||
mAdapter.update(events);
|
||||
UsageEvents events = mUsageStatsManager.queryEvents(mLastTime, now);
|
||||
long lastEventTime = mAdapter.update(events);
|
||||
if (lastEventTime >= 0) {
|
||||
mLastTime = lastEventTime + 1;
|
||||
}
|
||||
mHandler.postDelayed(this, 1000 * 5);
|
||||
}
|
||||
|
||||
private class Adapter extends BaseAdapter {
|
||||
private static final int MAX_EVENTS = 50;
|
||||
private final CircularArray<UsageEvents.Event> mEvents = new CircularArray<>(MAX_EVENTS);
|
||||
|
||||
private final ArrayList<UsageEvents.Event> mEvents = new ArrayList<>();
|
||||
|
||||
public void update(UsageEvents results) {
|
||||
mEvents.clear();
|
||||
public long update(UsageEvents results) {
|
||||
long lastTimeStamp = -1;
|
||||
while (results.hasNextEvent()) {
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
results.getNextEvent(event);
|
||||
mEvents.add(event);
|
||||
lastTimeStamp = event.getTimeStamp();
|
||||
if (mEvents.size() == MAX_EVENTS) {
|
||||
mEvents.popLast();
|
||||
}
|
||||
mEvents.addFirst(event);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
return lastTimeStamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +96,7 @@ public class UsageLogActivity extends ListActivity implements Runnable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
public UsageEvents.Event getItem(int position) {
|
||||
return mEvents.get(position);
|
||||
}
|
||||
|
||||
@@ -94,42 +105,73 @@ public class UsageLogActivity extends ListActivity implements Runnable {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
final int eventType = getItem(position).getEventType();
|
||||
if (eventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final UsageEvents.Event event = getItem(position);
|
||||
|
||||
final ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(UsageLogActivity.this)
|
||||
.inflate(R.layout.row_item, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
holder.state = (TextView) convertView.findViewById(android.R.id.text2);
|
||||
|
||||
if (event.getEventType() == UsageEvents.Event.CONFIGURATION_CHANGE) {
|
||||
convertView = LayoutInflater.from(UsageLogActivity.this)
|
||||
.inflate(R.layout.config_row_item, parent, false);
|
||||
holder.config = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
} else {
|
||||
convertView = LayoutInflater.from(UsageLogActivity.this)
|
||||
.inflate(R.layout.row_item, parent, false);
|
||||
holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
holder.state = (TextView) convertView.findViewById(android.R.id.text2);
|
||||
}
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
holder.packageName.setText(mEvents.get(position).getPackageName());
|
||||
String state;
|
||||
switch (mEvents.get(position).getEventType()) {
|
||||
if (holder.packageName != null) {
|
||||
holder.packageName.setText(event.getPackageName());
|
||||
}
|
||||
|
||||
if (holder.state != null) {
|
||||
holder.state.setText(eventToString(event.getEventType()));
|
||||
}
|
||||
|
||||
if (holder.config != null &&
|
||||
event.getEventType() == UsageEvents.Event.CONFIGURATION_CHANGE) {
|
||||
holder.config.setText(event.getConfiguration().toString());
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private String eventToString(int eventType) {
|
||||
switch (eventType) {
|
||||
case UsageEvents.Event.MOVE_TO_FOREGROUND:
|
||||
state = "Foreground";
|
||||
break;
|
||||
return "Foreground";
|
||||
|
||||
case UsageEvents.Event.MOVE_TO_BACKGROUND:
|
||||
state = "Background";
|
||||
break;
|
||||
return "Background";
|
||||
|
||||
case UsageEvents.Event.CONFIGURATION_CHANGE:
|
||||
return "Config change";
|
||||
|
||||
default:
|
||||
state = "Unknown: " + mEvents.get(position).getEventType();
|
||||
break;
|
||||
return "Unknown: " + eventType;
|
||||
}
|
||||
holder.state.setText(state);
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
public TextView packageName;
|
||||
public TextView state;
|
||||
public TextView config;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user