Merge "Rewrite app standby stats"
This commit is contained in:
committed by
Android (Google) Code Review
commit
046cc21b75
@@ -46,20 +46,6 @@ public final class UsageStats implements Parcelable {
|
||||
*/
|
||||
public long mLastTimeUsed;
|
||||
|
||||
/**
|
||||
* The last time the package was used via implicit, non-user initiated actions (service
|
||||
* was bound, etc).
|
||||
* {@hide}
|
||||
*/
|
||||
public long mLastTimeSystemUsed;
|
||||
|
||||
/**
|
||||
* Last time the package was used and the beginning of the idle countdown.
|
||||
* This uses a different timebase that is about how much the device has been in use in general.
|
||||
* {@hide}
|
||||
*/
|
||||
public long mBeginIdleTime;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
@@ -89,8 +75,6 @@ public final class UsageStats implements Parcelable {
|
||||
mTotalTimeInForeground = stats.mTotalTimeInForeground;
|
||||
mLaunchCount = stats.mLaunchCount;
|
||||
mLastEvent = stats.mLastEvent;
|
||||
mBeginIdleTime = stats.mBeginIdleTime;
|
||||
mLastTimeSystemUsed = stats.mLastTimeSystemUsed;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
@@ -126,25 +110,6 @@ public final class UsageStats implements Parcelable {
|
||||
return mLastTimeUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Get the last time this package was used by the system (not the user). This can be different
|
||||
* from {@link #getLastTimeUsed()} when the system binds to one of this package's services.
|
||||
* See {@link System#currentTimeMillis()}.
|
||||
*/
|
||||
public long getLastTimeSystemUsed() {
|
||||
return mLastTimeSystemUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Get the last time this package was active, measured in milliseconds. This timestamp
|
||||
* uses a timebase that represents how much the device was used and not wallclock time.
|
||||
*/
|
||||
public long getBeginIdleTime() {
|
||||
return mBeginIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total time this package spent in the foreground, measured in milliseconds.
|
||||
*/
|
||||
@@ -172,8 +137,6 @@ public final class UsageStats implements Parcelable {
|
||||
// regards to their mEndTimeStamp.
|
||||
mLastEvent = right.mLastEvent;
|
||||
mLastTimeUsed = right.mLastTimeUsed;
|
||||
mBeginIdleTime = right.mBeginIdleTime;
|
||||
mLastTimeSystemUsed = right.mLastTimeSystemUsed;
|
||||
}
|
||||
mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
|
||||
mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
|
||||
@@ -195,8 +158,6 @@ public final class UsageStats implements Parcelable {
|
||||
dest.writeLong(mTotalTimeInForeground);
|
||||
dest.writeInt(mLaunchCount);
|
||||
dest.writeInt(mLastEvent);
|
||||
dest.writeLong(mBeginIdleTime);
|
||||
dest.writeLong(mLastTimeSystemUsed);
|
||||
}
|
||||
|
||||
public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
|
||||
@@ -210,8 +171,6 @@ public final class UsageStats implements Parcelable {
|
||||
stats.mTotalTimeInForeground = in.readLong();
|
||||
stats.mLaunchCount = in.readInt();
|
||||
stats.mLastEvent = in.readInt();
|
||||
stats.mBeginIdleTime = in.readLong();
|
||||
stats.mLastTimeSystemUsed = in.readLong();
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
services.core \
|
||||
services.devicepolicy \
|
||||
services.net \
|
||||
services.usage \
|
||||
easymocklib \
|
||||
guava \
|
||||
android-support-test \
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 com.android.server.usage;
|
||||
|
||||
import android.os.FileUtils;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class AppIdleHistoryTests extends AndroidTestCase {
|
||||
|
||||
File mStorageDir;
|
||||
|
||||
final static String PACKAGE_1 = "com.android.testpackage1";
|
||||
final static String PACKAGE_2 = "com.android.testpackage2";
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mStorageDir = new File(getContext().getFilesDir(), "appidle");
|
||||
mStorageDir.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
FileUtils.deleteContents(mStorageDir);
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testFilesCreation() {
|
||||
final int userId = 0;
|
||||
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);
|
||||
|
||||
aih.updateDisplayLocked(true, /* elapsedRealtime= */ 1000);
|
||||
aih.updateDisplayLocked(false, /* elapsedRealtime= */ 2000);
|
||||
// Screen On time file should be written right away
|
||||
assertTrue(aih.getScreenOnTimeFile().exists());
|
||||
|
||||
aih.writeAppIdleTimesLocked(userId);
|
||||
// stats file should be written now
|
||||
assertTrue(new File(new File(mStorageDir, "users/" + userId),
|
||||
AppIdleHistory.APP_IDLE_FILENAME).exists());
|
||||
}
|
||||
|
||||
public void testScreenOnTime() {
|
||||
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
|
||||
aih.updateDisplayLocked(false, 2000);
|
||||
assertEquals(aih.getScreenOnTimeLocked(2000), 0);
|
||||
aih.updateDisplayLocked(true, 3000);
|
||||
assertEquals(aih.getScreenOnTimeLocked(4000), 1000);
|
||||
assertEquals(aih.getScreenOnTimeLocked(5000), 2000);
|
||||
aih.updateDisplayLocked(false, 6000);
|
||||
// Screen on time should not keep progressing with screen is off
|
||||
assertEquals(aih.getScreenOnTimeLocked(7000), 3000);
|
||||
assertEquals(aih.getScreenOnTimeLocked(8000), 3000);
|
||||
aih.writeElapsedTimeLocked();
|
||||
|
||||
// Check if the screen on time is persisted across instantiations
|
||||
AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0);
|
||||
assertEquals(aih2.getScreenOnTimeLocked(11000), 3000);
|
||||
aih2.updateDisplayLocked(true, 4000);
|
||||
aih2.updateDisplayLocked(false, 5000);
|
||||
assertEquals(aih2.getScreenOnTimeLocked(13000), 4000);
|
||||
}
|
||||
|
||||
public void testPackageEvents() {
|
||||
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
|
||||
aih.setThresholds(4000, 1000);
|
||||
aih.updateDisplayLocked(true, 1000);
|
||||
// App is not-idle by default
|
||||
assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 1500));
|
||||
// Still not idle
|
||||
assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 3000));
|
||||
// Idle now
|
||||
assertTrue(aih.isIdleLocked(PACKAGE_1, 0, 8000));
|
||||
// Not idle
|
||||
assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 9000));
|
||||
|
||||
// Screen off
|
||||
aih.updateDisplayLocked(false, 9100);
|
||||
// Still idle after 10 seconds because screen hasn't been on long enough
|
||||
assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 20000));
|
||||
aih.updateDisplayLocked(true, 21000);
|
||||
assertTrue(aih.isIdleLocked(PACKAGE_2, 0, 23000));
|
||||
}
|
||||
}
|
||||
@@ -16,19 +16,45 @@
|
||||
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TimeUtils;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Keeps track of recent active state changes in apps.
|
||||
* Access should be guarded by a lock by the caller.
|
||||
*/
|
||||
public class AppIdleHistory {
|
||||
|
||||
private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>();
|
||||
private long lastPeriod = 0;
|
||||
private static final String TAG = "AppIdleHistory";
|
||||
|
||||
// History for all users and all packages
|
||||
private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>();
|
||||
private long mLastPeriod = 0;
|
||||
private static final long ONE_MINUTE = 60 * 1000;
|
||||
private static final int HISTORY_SIZE = 100;
|
||||
private static final int FLAG_LAST_STATE = 2;
|
||||
@@ -36,77 +62,353 @@ public class AppIdleHistory {
|
||||
private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE
|
||||
: 60 * ONE_MINUTE;
|
||||
|
||||
public void addEntry(String packageName, int userId, boolean idle, long timeNow) {
|
||||
ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
|
||||
byte[] packageHistory = getPackageHistory(userHistory, packageName);
|
||||
@VisibleForTesting
|
||||
static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
|
||||
private static final String TAG_PACKAGES = "packages";
|
||||
private static final String TAG_PACKAGE = "package";
|
||||
private static final String ATTR_NAME = "name";
|
||||
// Screen on timebase time when app was last used
|
||||
private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
|
||||
// Elapsed timebase time when app was last used
|
||||
private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
|
||||
|
||||
long thisPeriod = timeNow / PERIOD_DURATION;
|
||||
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
|
||||
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
|
||||
private long mElapsedDuration; // Total device on duration since device was "born"
|
||||
|
||||
// screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
|
||||
private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
|
||||
private long mScreenOnDuration; // Total screen on duration since device was "born"
|
||||
|
||||
private long mElapsedTimeThreshold;
|
||||
private long mScreenOnTimeThreshold;
|
||||
private final File mStorageDir;
|
||||
|
||||
private boolean mScreenOn;
|
||||
|
||||
private static class PackageHistory {
|
||||
final byte[] recent = new byte[HISTORY_SIZE];
|
||||
long lastUsedElapsedTime;
|
||||
long lastUsedScreenTime;
|
||||
}
|
||||
|
||||
AppIdleHistory(long elapsedRealtime) {
|
||||
this(Environment.getDataSystemDirectory(), elapsedRealtime);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AppIdleHistory(File storageDir, long elapsedRealtime) {
|
||||
mElapsedSnapshot = elapsedRealtime;
|
||||
mScreenOnSnapshot = elapsedRealtime;
|
||||
mStorageDir = storageDir;
|
||||
readScreenOnTimeLocked();
|
||||
}
|
||||
|
||||
public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
|
||||
mElapsedTimeThreshold = elapsedTimeThreshold;
|
||||
mScreenOnTimeThreshold = screenOnTimeThreshold;
|
||||
}
|
||||
|
||||
public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) {
|
||||
if (screenOn == mScreenOn) return;
|
||||
|
||||
mScreenOn = screenOn;
|
||||
if (mScreenOn) {
|
||||
mScreenOnSnapshot = elapsedRealtime;
|
||||
} else {
|
||||
mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
|
||||
mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
|
||||
writeScreenOnTimeLocked();
|
||||
mElapsedSnapshot = elapsedRealtime;
|
||||
}
|
||||
}
|
||||
|
||||
public long getScreenOnTimeLocked(long elapsedRealtime) {
|
||||
long screenOnTime = mScreenOnDuration;
|
||||
if (mScreenOn) {
|
||||
screenOnTime += elapsedRealtime - mScreenOnSnapshot;
|
||||
}
|
||||
return screenOnTime;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
File getScreenOnTimeFile() {
|
||||
return new File(mStorageDir, "screen_on_time");
|
||||
}
|
||||
|
||||
private void readScreenOnTimeLocked() {
|
||||
File screenOnTimeFile = getScreenOnTimeFile();
|
||||
if (screenOnTimeFile.exists()) {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
|
||||
mScreenOnDuration = Long.parseLong(reader.readLine());
|
||||
mElapsedDuration = Long.parseLong(reader.readLine());
|
||||
reader.close();
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
}
|
||||
} else {
|
||||
writeScreenOnTimeLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeScreenOnTimeLocked() {
|
||||
AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = screenOnTimeFile.startWrite();
|
||||
fos.write((Long.toString(mScreenOnDuration) + "\n"
|
||||
+ Long.toString(mElapsedDuration) + "\n").getBytes());
|
||||
screenOnTimeFile.finishWrite(fos);
|
||||
} catch (IOException ioe) {
|
||||
screenOnTimeFile.failWrite(fos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called periodically to keep track of elapsed time when app idle times are written
|
||||
*/
|
||||
public void writeElapsedTimeLocked() {
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
// Only bump up and snapshot the elapsed time. Don't change screen on duration.
|
||||
mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
|
||||
mElapsedSnapshot = elapsedRealtime;
|
||||
writeScreenOnTimeLocked();
|
||||
}
|
||||
|
||||
public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) {
|
||||
ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
|
||||
PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
|
||||
elapsedRealtime);
|
||||
|
||||
shiftHistoryToNow(userHistory, elapsedRealtime);
|
||||
|
||||
packageHistory.lastUsedElapsedTime = mElapsedDuration
|
||||
+ (elapsedRealtime - mElapsedSnapshot);
|
||||
packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
|
||||
packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
|
||||
}
|
||||
|
||||
public void setIdle(String packageName, int userId, long elapsedRealtime) {
|
||||
ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
|
||||
PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
|
||||
elapsedRealtime);
|
||||
|
||||
shiftHistoryToNow(userHistory, elapsedRealtime);
|
||||
|
||||
packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
|
||||
}
|
||||
|
||||
private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory,
|
||||
long elapsedRealtime) {
|
||||
long thisPeriod = elapsedRealtime / PERIOD_DURATION;
|
||||
// Has the period switched over? Slide all users' package histories
|
||||
if (lastPeriod != 0 && lastPeriod < thisPeriod
|
||||
&& (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) {
|
||||
int diff = (int) (thisPeriod - lastPeriod);
|
||||
if (mLastPeriod != 0 && mLastPeriod < thisPeriod
|
||||
&& (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) {
|
||||
int diff = (int) (thisPeriod - mLastPeriod);
|
||||
final int NUSERS = mIdleHistory.size();
|
||||
for (int u = 0; u < NUSERS; u++) {
|
||||
userHistory = mIdleHistory.valueAt(u);
|
||||
for (byte[] history : userHistory.values()) {
|
||||
for (PackageHistory idleState : userHistory.values()) {
|
||||
// Shift left
|
||||
System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff);
|
||||
System.arraycopy(idleState.recent, diff, idleState.recent, 0,
|
||||
HISTORY_SIZE - diff);
|
||||
// Replicate last state across the diff
|
||||
for (int i = 0; i < diff; i++) {
|
||||
history[HISTORY_SIZE - i - 1] =
|
||||
(byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
|
||||
idleState.recent[HISTORY_SIZE - i - 1] =
|
||||
(byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastPeriod = thisPeriod;
|
||||
if (!idle) {
|
||||
packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
|
||||
} else {
|
||||
packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
|
||||
}
|
||||
mLastPeriod = thisPeriod;
|
||||
}
|
||||
|
||||
private ArrayMap<String, byte[]> getUserHistory(int userId) {
|
||||
ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
|
||||
private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) {
|
||||
ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
|
||||
if (userHistory == null) {
|
||||
userHistory = new ArrayMap<>();
|
||||
mIdleHistory.put(userId, userHistory);
|
||||
readAppIdleTimesLocked(userId, userHistory);
|
||||
}
|
||||
return userHistory;
|
||||
}
|
||||
|
||||
private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) {
|
||||
byte[] packageHistory = userHistory.get(packageName);
|
||||
private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory,
|
||||
String packageName, long elapsedRealtime) {
|
||||
PackageHistory packageHistory = userHistory.get(packageName);
|
||||
if (packageHistory == null) {
|
||||
packageHistory = new byte[HISTORY_SIZE];
|
||||
packageHistory = new PackageHistory();
|
||||
packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime);
|
||||
packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
|
||||
userHistory.put(packageName, packageHistory);
|
||||
}
|
||||
return packageHistory;
|
||||
}
|
||||
|
||||
public void removeUser(int userId) {
|
||||
public void onUserRemoved(int userId) {
|
||||
mIdleHistory.remove(userId);
|
||||
}
|
||||
|
||||
public boolean isIdle(int userId, String packageName) {
|
||||
ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
|
||||
byte[] packageHistory = getPackageHistory(userHistory, packageName);
|
||||
return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0;
|
||||
public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) {
|
||||
ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
|
||||
PackageHistory packageHistory =
|
||||
getPackageHistoryLocked(userHistory, packageName, elapsedRealtime);
|
||||
if (packageHistory == null) {
|
||||
return false; // Default to not idle
|
||||
} else {
|
||||
return hasPassedThresholdsLocked(packageHistory, elapsedRealtime);
|
||||
}
|
||||
}
|
||||
|
||||
private long getElapsedTimeLocked(long elapsedRealtime) {
|
||||
return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
|
||||
}
|
||||
|
||||
public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) {
|
||||
ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
|
||||
PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
|
||||
elapsedRealtime);
|
||||
packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime)
|
||||
- mElapsedTimeThreshold;
|
||||
packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime)
|
||||
- (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
|
||||
}
|
||||
|
||||
private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
|
||||
return (packageHistory.lastUsedScreenTime
|
||||
<= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
|
||||
&& (packageHistory.lastUsedElapsedTime
|
||||
<= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold);
|
||||
}
|
||||
|
||||
private File getUserFile(int userId) {
|
||||
return new File(new File(new File(mStorageDir, "users"),
|
||||
Integer.toString(userId)), APP_IDLE_FILENAME);
|
||||
}
|
||||
|
||||
private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
|
||||
fis = appIdleFile.openRead();
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(fis, StandardCharsets.UTF_8.name());
|
||||
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.START_TAG
|
||||
&& type != XmlPullParser.END_DOCUMENT) {
|
||||
// Skip
|
||||
}
|
||||
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
Slog.e(TAG, "Unable to read app idle file for user " + userId);
|
||||
return;
|
||||
}
|
||||
if (!parser.getName().equals(TAG_PACKAGES)) {
|
||||
return;
|
||||
}
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
final String name = parser.getName();
|
||||
if (name.equals(TAG_PACKAGE)) {
|
||||
final String packageName = parser.getAttributeValue(null, ATTR_NAME);
|
||||
PackageHistory packageHistory = new PackageHistory();
|
||||
packageHistory.lastUsedElapsedTime =
|
||||
Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
|
||||
packageHistory.lastUsedScreenTime =
|
||||
Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
|
||||
userHistory.put(packageName, packageHistory);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
Slog.e(TAG, "Unable to read app idle file for user " + userId);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(fis);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeAppIdleTimesLocked(int userId) {
|
||||
FileOutputStream fos = null;
|
||||
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
|
||||
try {
|
||||
fos = appIdleFile.startWrite();
|
||||
final BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
|
||||
FastXmlSerializer xml = new FastXmlSerializer();
|
||||
xml.setOutput(bos, StandardCharsets.UTF_8.name());
|
||||
xml.startDocument(null, true);
|
||||
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||
|
||||
xml.startTag(null, TAG_PACKAGES);
|
||||
|
||||
ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId);
|
||||
final int N = userHistory.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
String packageName = userHistory.keyAt(i);
|
||||
PackageHistory history = userHistory.valueAt(i);
|
||||
xml.startTag(null, TAG_PACKAGE);
|
||||
xml.attribute(null, ATTR_NAME, packageName);
|
||||
xml.attribute(null, ATTR_ELAPSED_IDLE,
|
||||
Long.toString(history.lastUsedElapsedTime));
|
||||
xml.attribute(null, ATTR_SCREEN_IDLE,
|
||||
Long.toString(history.lastUsedScreenTime));
|
||||
xml.endTag(null, TAG_PACKAGE);
|
||||
}
|
||||
|
||||
xml.endTag(null, TAG_PACKAGES);
|
||||
xml.endDocument();
|
||||
appIdleFile.finishWrite(fos);
|
||||
} catch (Exception e) {
|
||||
appIdleFile.failWrite(fos);
|
||||
Slog.e(TAG, "Error writing app idle file for user " + userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(IndentingPrintWriter idpw, int userId) {
|
||||
ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
|
||||
idpw.println("Package idle stats:");
|
||||
idpw.increaseIndent();
|
||||
ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime);
|
||||
final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime);
|
||||
if (userHistory == null) return;
|
||||
final int P = userHistory.size();
|
||||
for (int p = 0; p < P; p++) {
|
||||
final String packageName = userHistory.keyAt(p);
|
||||
final byte[] history = userHistory.valueAt(p);
|
||||
final PackageHistory packageHistory = userHistory.valueAt(p);
|
||||
idpw.print("package=" + packageName);
|
||||
idpw.print(" lastUsedElapsed=");
|
||||
TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw);
|
||||
idpw.print(" lastUsedScreenOn=");
|
||||
TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw);
|
||||
idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
|
||||
idpw.println();
|
||||
}
|
||||
idpw.println();
|
||||
idpw.print("totalElapsedTime=");
|
||||
TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw);
|
||||
idpw.println();
|
||||
idpw.print("totalScreenOnTime=");
|
||||
TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw);
|
||||
idpw.println();
|
||||
idpw.decreaseIndent();
|
||||
}
|
||||
|
||||
public void dumpHistory(IndentingPrintWriter idpw, int userId) {
|
||||
ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
if (userHistory == null) return;
|
||||
final int P = userHistory.size();
|
||||
for (int p = 0; p < P; p++) {
|
||||
final String packageName = userHistory.keyAt(p);
|
||||
final byte[] history = userHistory.valueAt(p).recent;
|
||||
for (int i = 0; i < HISTORY_SIZE; i++) {
|
||||
idpw.print(history[i] == 0 ? '.' : 'A');
|
||||
}
|
||||
idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
|
||||
idpw.print(" " + packageName);
|
||||
idpw.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ class IntervalStats {
|
||||
usageStats.mPackageName = getCachedStringRef(packageName);
|
||||
usageStats.mBeginTimeStamp = beginTime;
|
||||
usageStats.mEndTimeStamp = endTime;
|
||||
usageStats.mBeginIdleTime = 0;
|
||||
packageStats.put(usageStats.mPackageName, usageStats);
|
||||
}
|
||||
return usageStats;
|
||||
@@ -113,7 +112,6 @@ class IntervalStats {
|
||||
if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
|
||||
usageStats.mLastTimeUsed = timeStamp;
|
||||
}
|
||||
usageStats.mLastTimeSystemUsed = timeStamp;
|
||||
usageStats.mEndTimeStamp = timeStamp;
|
||||
|
||||
if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
|
||||
@@ -123,22 +121,6 @@ class IntervalStats {
|
||||
endTime = timeStamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the last active time for the package. The timestamp uses a timebase that
|
||||
* tracks the device usage time.
|
||||
* @param packageName
|
||||
* @param timeStamp
|
||||
*/
|
||||
void updateBeginIdleTime(String packageName, long timeStamp) {
|
||||
UsageStats usageStats = getOrCreateUsageStats(packageName);
|
||||
usageStats.mBeginIdleTime = timeStamp;
|
||||
}
|
||||
|
||||
void updateSystemLastUsedTime(String packageName, long lastUsedTime) {
|
||||
UsageStats usageStats = getOrCreateUsageStats(packageName);
|
||||
usageStats.mLastTimeSystemUsed = lastUsedTime;
|
||||
}
|
||||
|
||||
void updateConfigurationStats(Configuration config, long timeStamp) {
|
||||
if (activeConfiguration != null) {
|
||||
ConfigurationStats activeStats = configurations.get(activeConfiguration);
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.display.DisplayManager;
|
||||
@@ -62,7 +63,6 @@ import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.KeyValueListParser;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
@@ -77,12 +77,8 @@ import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -106,7 +102,7 @@ public class UsageStatsService extends SystemService implements
|
||||
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
|
||||
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
|
||||
|
||||
long mAppIdleDurationMillis;
|
||||
long mAppIdleScreenThresholdMillis;
|
||||
long mCheckIdleIntervalMillis;
|
||||
long mAppIdleWallclockThresholdMillis;
|
||||
long mAppIdleParoleIntervalMillis;
|
||||
@@ -147,11 +143,8 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
private volatile boolean mPendingOneTimeCheckIdleStates;
|
||||
|
||||
long mScreenOnTime;
|
||||
long mLastScreenOnEventRealtime;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
|
||||
private AppIdleHistory mAppIdleHistory;
|
||||
|
||||
private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
|
||||
mPackageAccessListeners = new ArrayList<>();
|
||||
@@ -191,8 +184,7 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
synchronized (mLock) {
|
||||
cleanUpRemovedUsersLocked();
|
||||
mLastScreenOnEventRealtime = SystemClock.elapsedRealtime();
|
||||
mScreenOnTime = readScreenOnTimeLocked();
|
||||
mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
mRealTimeSnapshot = SystemClock.elapsedRealtime();
|
||||
@@ -221,7 +213,7 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
|
||||
synchronized (mLock) {
|
||||
updateDisplayLocked();
|
||||
mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
if (mPendingOneTimeCheckIdleStates) {
|
||||
@@ -232,6 +224,11 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDisplayOn() {
|
||||
return mDisplayManager
|
||||
.getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
|
||||
}
|
||||
|
||||
private class UserActionsReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
@@ -274,7 +271,8 @@ public class UsageStatsService extends SystemService implements
|
||||
@Override public void onDisplayChanged(int displayId) {
|
||||
if (displayId == Display.DEFAULT_DISPLAY) {
|
||||
synchronized (UsageStatsService.this.mLock) {
|
||||
updateDisplayLocked();
|
||||
mAppIdleHistory.updateDisplayLocked(isDisplayOn(),
|
||||
SystemClock.elapsedRealtime());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,8 +289,25 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAppIdleRollingWindowDurationMillis() {
|
||||
return mAppIdleWallclockThresholdMillis * 2;
|
||||
public void onNewUpdate(int userId) {
|
||||
initializeDefaultsForSystemApps(userId);
|
||||
}
|
||||
|
||||
private void initializeDefaultsForSystemApps(int userId) {
|
||||
Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackagesAsUser(
|
||||
PackageManager.MATCH_DISABLED_COMPONENTS
|
||||
| PackageManager.MATCH_UNINSTALLED_PACKAGES,
|
||||
userId);
|
||||
final int packageCount = packages.size();
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
final PackageInfo pi = packages.get(i);
|
||||
String packageName = pi.packageName;
|
||||
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
|
||||
mAppIdleHistory.reportUsageLocked(packageName, userId, elapsedRealtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpRemovedUsersLocked() {
|
||||
@@ -350,7 +365,7 @@ public class UsageStatsService extends SystemService implements
|
||||
if (timeLeft < 0) {
|
||||
timeLeft = 0;
|
||||
}
|
||||
mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft / 10);
|
||||
mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
|
||||
}
|
||||
|
||||
private void postParoleEndTimeout() {
|
||||
@@ -400,28 +415,27 @@ public class UsageStatsService extends SystemService implements
|
||||
return;
|
||||
}
|
||||
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
for (int i = 0; i < userIds.length; i++) {
|
||||
final int userId = userIds[i];
|
||||
List<PackageInfo> packages =
|
||||
getContext().getPackageManager().getInstalledPackagesAsUser(
|
||||
PackageManager.GET_DISABLED_COMPONENTS
|
||||
| PackageManager.GET_UNINSTALLED_PACKAGES,
|
||||
PackageManager.MATCH_DISABLED_COMPONENTS
|
||||
| PackageManager.MATCH_UNINSTALLED_PACKAGES,
|
||||
userId);
|
||||
synchronized (mLock) {
|
||||
final long timeNow = checkAndGetTimeLocked();
|
||||
final long screenOnTime = getScreenOnTimeLocked();
|
||||
UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId,
|
||||
timeNow);
|
||||
final int packageCount = packages.size();
|
||||
for (int p = 0; p < packageCount; p++) {
|
||||
final PackageInfo pi = packages.get(p);
|
||||
final String packageName = pi.packageName;
|
||||
final boolean isIdle = isAppIdleFiltered(packageName,
|
||||
UserHandle.getAppId(pi.applicationInfo.uid),
|
||||
userId, service, timeNow, screenOnTime);
|
||||
userId, elapsedRealtime);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
|
||||
userId, isIdle ? 1 : 0, packageName));
|
||||
mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
|
||||
if (isIdle) {
|
||||
mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,62 +472,6 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
void updateDisplayLocked() {
|
||||
boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
|
||||
== Display.STATE_ON;
|
||||
|
||||
if (screenOn == mScreenOn) return;
|
||||
|
||||
mScreenOn = screenOn;
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
if (mScreenOn) {
|
||||
mLastScreenOnEventRealtime = now;
|
||||
} else {
|
||||
mScreenOnTime += now - mLastScreenOnEventRealtime;
|
||||
writeScreenOnTimeLocked(mScreenOnTime);
|
||||
}
|
||||
}
|
||||
|
||||
long getScreenOnTimeLocked() {
|
||||
long screenOnTime = mScreenOnTime;
|
||||
if (mScreenOn) {
|
||||
screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime;
|
||||
}
|
||||
return screenOnTime;
|
||||
}
|
||||
|
||||
private File getScreenOnTimeFile() {
|
||||
return new File(mUsageStatsDir, UserHandle.USER_SYSTEM + "/screen_on_time");
|
||||
}
|
||||
|
||||
private long readScreenOnTimeLocked() {
|
||||
long screenOnTime = 0;
|
||||
File screenOnTimeFile = getScreenOnTimeFile();
|
||||
if (screenOnTimeFile.exists()) {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
|
||||
screenOnTime = Long.parseLong(reader.readLine());
|
||||
reader.close();
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
}
|
||||
} else {
|
||||
writeScreenOnTimeLocked(screenOnTime);
|
||||
}
|
||||
return screenOnTime;
|
||||
}
|
||||
|
||||
private void writeScreenOnTimeLocked(long screenOnTime) {
|
||||
AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = screenOnTimeFile.startWrite();
|
||||
fos.write(Long.toString(screenOnTime).getBytes());
|
||||
screenOnTimeFile.finishWrite(fos);
|
||||
} catch (IOException ioe) {
|
||||
screenOnTimeFile.failWrite(fos);
|
||||
}
|
||||
}
|
||||
|
||||
void onDeviceIdleModeChanged() {
|
||||
final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
|
||||
if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
|
||||
@@ -549,7 +507,7 @@ public class UsageStatsService extends SystemService implements
|
||||
if (service == null) {
|
||||
service = new UserUsageStatsService(getContext(), userId,
|
||||
new File(mUsageStatsDir, Integer.toString(userId)), this);
|
||||
service.init(currentTimeMillis, getScreenOnTimeLocked());
|
||||
service.init(currentTimeMillis);
|
||||
mUserState.put(userId, service);
|
||||
}
|
||||
return service;
|
||||
@@ -569,8 +527,7 @@ public class UsageStatsService extends SystemService implements
|
||||
final int userCount = mUserState.size();
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
final UserUsageStatsService service = mUserState.valueAt(i);
|
||||
service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(),
|
||||
false);
|
||||
service.onTimeChanged(expectedSystemTime, actualSystemTime);
|
||||
}
|
||||
mRealTimeSnapshot = actualRealtime;
|
||||
mSystemTimeSnapshot = actualSystemTime;
|
||||
@@ -602,26 +559,26 @@ public class UsageStatsService extends SystemService implements
|
||||
void reportEvent(UsageEvents.Event event, int userId) {
|
||||
synchronized (mLock) {
|
||||
final long timeNow = checkAndGetTimeLocked();
|
||||
final long screenOnTime = getScreenOnTimeLocked();
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
convertToSystemTimeLocked(event);
|
||||
|
||||
final UserUsageStatsService service =
|
||||
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
|
||||
final long beginIdleTime = service.getBeginIdleTime(event.mPackage);
|
||||
final long lastUsedTime = service.getSystemLastUsedTime(event.mPackage);
|
||||
final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
|
||||
lastUsedTime, screenOnTime, timeNow);
|
||||
service.reportEvent(event, screenOnTime);
|
||||
// TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
|
||||
// about apps that are on some kind of whitelist anyway.
|
||||
final boolean previouslyIdle = mAppIdleHistory.isIdleLocked(
|
||||
event.mPackage, userId, elapsedRealtime);
|
||||
service.reportEvent(event);
|
||||
// Inform listeners if necessary
|
||||
if ((event.mEventType == Event.MOVE_TO_FOREGROUND
|
||||
|| event.mEventType == Event.MOVE_TO_BACKGROUND
|
||||
|| event.mEventType == Event.SYSTEM_INTERACTION
|
||||
|| event.mEventType == Event.USER_INTERACTION)) {
|
||||
mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime);
|
||||
if (previouslyIdle) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
|
||||
/* idle = */ 0, event.mPackage));
|
||||
notifyBatteryStats(event.mPackage, userId, false);
|
||||
mAppIdleHistory.addEntry(event.mPackage, userId, false, timeNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,28 +612,23 @@ public class UsageStatsService extends SystemService implements
|
||||
* the threshold for idle.
|
||||
*/
|
||||
void forceIdleState(String packageName, int userId, boolean idle) {
|
||||
final int appId = getAppId(packageName);
|
||||
if (appId < 0) return;
|
||||
synchronized (mLock) {
|
||||
final long timeNow = checkAndGetTimeLocked();
|
||||
final long screenOnTime = getScreenOnTimeLocked();
|
||||
final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
|
||||
final UserUsageStatsService service =
|
||||
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
|
||||
final long beginIdleTime = service.getBeginIdleTime(packageName);
|
||||
final long lastUsedTime = service.getSystemLastUsedTime(packageName);
|
||||
final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
|
||||
lastUsedTime, screenOnTime, timeNow);
|
||||
service.setBeginIdleTime(packageName, deviceUsageTime);
|
||||
service.setSystemLastUsedTime(packageName,
|
||||
timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
|
||||
final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
|
||||
userId, elapsedRealtime);
|
||||
mAppIdleHistory.setIdleLocked(packageName, userId, idle, elapsedRealtime);
|
||||
final boolean stillIdle = isAppIdleFiltered(packageName, appId,
|
||||
userId, elapsedRealtime);
|
||||
// Inform listeners if necessary
|
||||
if (previouslyIdle != idle) {
|
||||
if (previouslyIdle != stillIdle) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
|
||||
/* idle = */ idle ? 1 : 0, packageName));
|
||||
if (!idle) {
|
||||
/* idle = */ stillIdle ? 1 : 0, packageName));
|
||||
if (!stillIdle) {
|
||||
notifyBatteryStats(packageName, userId, idle);
|
||||
}
|
||||
mAppIdleHistory.addEntry(packageName, userId, idle, timeNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -693,10 +645,11 @@ public class UsageStatsService extends SystemService implements
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
void removeUser(int userId) {
|
||||
void onUserRemoved(int userId) {
|
||||
synchronized (mLock) {
|
||||
Slog.i(TAG, "Removing user " + userId + " and all data.");
|
||||
mUserState.remove(userId);
|
||||
mAppIdleHistory.onUserRemoved(userId);
|
||||
cleanUpRemovedUsersLocked();
|
||||
}
|
||||
}
|
||||
@@ -750,29 +703,12 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAppIdleUnfiltered(String packageName, UserUsageStatsService userService,
|
||||
long timeNow, long screenOnTime) {
|
||||
private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
|
||||
synchronized (mLock) {
|
||||
long beginIdleTime = userService.getBeginIdleTime(packageName);
|
||||
long lastUsedTime = userService.getSystemLastUsedTime(packageName);
|
||||
return hasPassedIdleTimeoutLocked(beginIdleTime, lastUsedTime, screenOnTime,
|
||||
timeNow);
|
||||
return mAppIdleHistory.isIdleLocked(packageName, userId, elapsedRealtime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beginIdleTime when the app was last used in device usage timebase
|
||||
* @param lastUsedTime wallclock time of when the app was last used
|
||||
* @param screenOnTime screen-on timebase time
|
||||
* @param currentTime current time in device usage timebase
|
||||
* @return whether it's been used far enough in the past to be considered inactive
|
||||
*/
|
||||
boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime,
|
||||
long screenOnTime, long currentTime) {
|
||||
return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis)
|
||||
&& (lastUsedTime <= currentTime - mAppIdleWallclockThresholdMillis);
|
||||
}
|
||||
|
||||
void addListener(AppIdleStateChangeListener listener) {
|
||||
synchronized (mLock) {
|
||||
if (!mPackageAccessListeners.contains(listener)) {
|
||||
@@ -787,32 +723,22 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) {
|
||||
int getAppId(String packageName) {
|
||||
try {
|
||||
ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
|
||||
PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
| PackageManager.MATCH_DISABLED_COMPONENTS);
|
||||
return ai.uid;
|
||||
} catch (NameNotFoundException re) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) {
|
||||
if (mAppIdleParoled) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES
|
||||
| PackageManager.GET_DISABLED_COMPONENTS);
|
||||
return isAppIdleFiltered(packageName, ai.uid, userId, timeNow);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) {
|
||||
final UserUsageStatsService userService;
|
||||
final long screenOnTime;
|
||||
synchronized (mLock) {
|
||||
if (timeNow == -1) {
|
||||
timeNow = checkAndGetTimeLocked();
|
||||
}
|
||||
userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
|
||||
screenOnTime = getScreenOnTimeLocked();
|
||||
}
|
||||
return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
|
||||
userService, timeNow, screenOnTime);
|
||||
return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -822,7 +748,7 @@ public class UsageStatsService extends SystemService implements
|
||||
* Called by interface impls.
|
||||
*/
|
||||
private boolean isAppIdleFiltered(String packageName, int appId, int userId,
|
||||
UserUsageStatsService userService, long timeNow, long screenOnTime) {
|
||||
long elapsedRealtime) {
|
||||
if (packageName == null) return false;
|
||||
// If not enabled at all, of course nobody is ever idle.
|
||||
if (!mAppIdleEnabled) {
|
||||
@@ -864,7 +790,7 @@ public class UsageStatsService extends SystemService implements
|
||||
return false;
|
||||
}
|
||||
|
||||
return isAppIdleUnfiltered(packageName, userService, timeNow, screenOnTime);
|
||||
return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
|
||||
}
|
||||
|
||||
int[] getIdleUidsForUser(int userId) {
|
||||
@@ -872,14 +798,7 @@ public class UsageStatsService extends SystemService implements
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
final long timeNow;
|
||||
final UserUsageStatsService userService;
|
||||
final long screenOnTime;
|
||||
synchronized (mLock) {
|
||||
timeNow = checkAndGetTimeLocked();
|
||||
userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
|
||||
screenOnTime = getScreenOnTimeLocked();
|
||||
}
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
|
||||
List<ApplicationInfo> apps;
|
||||
try {
|
||||
@@ -899,12 +818,12 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
// Now resolve all app state. Iterating over all apps, keeping track of how many
|
||||
// we find for each uid and how many of those are idle.
|
||||
for (int i = apps.size()-1; i >= 0; i--) {
|
||||
for (int i = apps.size() - 1; i >= 0; i--) {
|
||||
ApplicationInfo ai = apps.get(i);
|
||||
|
||||
// Check whether this app is idle.
|
||||
boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
|
||||
userId, userService, timeNow, screenOnTime);
|
||||
userId, elapsedRealtime);
|
||||
|
||||
int index = uidStates.indexOfKey(ai.uid);
|
||||
if (index < 0) {
|
||||
@@ -990,8 +909,11 @@ public class UsageStatsService extends SystemService implements
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
UserUsageStatsService service = mUserState.valueAt(i);
|
||||
service.persistActiveStats();
|
||||
mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
|
||||
}
|
||||
|
||||
// Persist elapsed time periodically, in case screen doesn't get toggled
|
||||
// until the next boot
|
||||
mAppIdleHistory.writeElapsedTimeLocked();
|
||||
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
|
||||
}
|
||||
|
||||
@@ -1000,7 +922,6 @@ public class UsageStatsService extends SystemService implements
|
||||
*/
|
||||
void dump(String[] args, PrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
final long screenOnTime = getScreenOnTimeLocked();
|
||||
IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
|
||||
ArraySet<String> argSet = new ArraySet<>();
|
||||
argSet.addAll(Arrays.asList(args));
|
||||
@@ -1011,27 +932,28 @@ public class UsageStatsService extends SystemService implements
|
||||
idpw.println();
|
||||
idpw.increaseIndent();
|
||||
if (argSet.contains("--checkin")) {
|
||||
mUserState.valueAt(i).checkin(idpw, screenOnTime);
|
||||
mUserState.valueAt(i).checkin(idpw);
|
||||
} else {
|
||||
mUserState.valueAt(i).dump(idpw, screenOnTime);
|
||||
mUserState.valueAt(i).dump(idpw);
|
||||
idpw.println();
|
||||
if (args.length > 0 && "history".equals(args[0])) {
|
||||
mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
|
||||
if (args.length > 0) {
|
||||
if ("history".equals(args[0])) {
|
||||
mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
|
||||
} else if ("flush".equals(args[0])) {
|
||||
UsageStatsService.this.flushToDiskLocked();
|
||||
pw.println("Flushed stats to disk");
|
||||
}
|
||||
}
|
||||
}
|
||||
mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
|
||||
idpw.decreaseIndent();
|
||||
}
|
||||
pw.print("Screen On Timebase: ");
|
||||
pw.print(screenOnTime);
|
||||
pw.print(" (");
|
||||
TimeUtils.formatDuration(screenOnTime, pw);
|
||||
pw.println(")");
|
||||
|
||||
pw.println();
|
||||
pw.println("Settings:");
|
||||
|
||||
pw.print(" mAppIdleDurationMillis=");
|
||||
TimeUtils.formatDuration(mAppIdleDurationMillis, pw);
|
||||
TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
|
||||
pw.println();
|
||||
|
||||
pw.print(" mAppIdleWallclockThresholdMillis=");
|
||||
@@ -1057,11 +979,6 @@ public class UsageStatsService extends SystemService implements
|
||||
pw.print("mLastAppIdleParoledTime=");
|
||||
TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
|
||||
pw.println();
|
||||
pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw);
|
||||
pw.println();
|
||||
pw.print("mLastScreenOnEventRealtime=");
|
||||
TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw);
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,7 +999,7 @@ public class UsageStatsService extends SystemService implements
|
||||
break;
|
||||
|
||||
case MSG_REMOVE_USER:
|
||||
removeUser(msg.arg1);
|
||||
onUserRemoved(msg.arg1);
|
||||
break;
|
||||
|
||||
case MSG_INFORM_LISTENERS:
|
||||
@@ -1179,13 +1096,13 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
// Default: 12 hours of screen-on time sans dream-time
|
||||
mAppIdleDurationMillis = mParser.getLong(KEY_IDLE_DURATION,
|
||||
mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
|
||||
COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
|
||||
|
||||
mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
|
||||
COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
|
||||
|
||||
mCheckIdleIntervalMillis = Math.min(mAppIdleDurationMillis / 4,
|
||||
mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
|
||||
COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
|
||||
|
||||
// Default: 24 hours between paroles
|
||||
@@ -1194,6 +1111,8 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
|
||||
COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
|
||||
mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
|
||||
mAppIdleScreenThresholdMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1284,7 +1203,8 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, -1);
|
||||
return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
|
||||
SystemClock.elapsedRealtime());
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -1304,11 +1224,9 @@ public class UsageStatsService extends SystemService implements
|
||||
"No permission to change app idle state");
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(packageName,
|
||||
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
|
||||
if (pi == null) return;
|
||||
final int appId = getAppId(packageName);
|
||||
if (appId < 0) return;
|
||||
UsageStatsService.this.setAppIdle(packageName, idle, userId);
|
||||
} catch (RemoteException re) {
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
@@ -1335,8 +1253,6 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
UsageStatsService.this.dump(args, pw);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1411,7 +1327,8 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
@Override
|
||||
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
|
||||
return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1);
|
||||
return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
|
||||
SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.app.usage.TimeSparseArray;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.content.res.Configuration;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
@@ -55,13 +54,11 @@ final class UsageStatsXmlV1 {
|
||||
|
||||
// Time attributes stored as an offset of the beginTime.
|
||||
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
|
||||
private static final String LAST_TIME_ACTIVE_SYSTEM_ATTR = "lastTimeActiveSystem";
|
||||
private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime";
|
||||
private static final String END_TIME_ATTR = "endTime";
|
||||
private static final String TIME_ATTR = "time";
|
||||
|
||||
private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
|
||||
throws XmlPullParserException, IOException {
|
||||
throws IOException {
|
||||
final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
|
||||
if (pkg == null) {
|
||||
throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
|
||||
@@ -72,20 +69,6 @@ final class UsageStatsXmlV1 {
|
||||
// Apply the offset to the beginTime to find the absolute time.
|
||||
stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
|
||||
parser, LAST_TIME_ACTIVE_ATTR);
|
||||
|
||||
final String lastTimeUsedSystem = parser.getAttributeValue(null,
|
||||
LAST_TIME_ACTIVE_SYSTEM_ATTR);
|
||||
if (TextUtils.isEmpty(lastTimeUsedSystem)) {
|
||||
// If the field isn't present, use the old one.
|
||||
stats.mLastTimeSystemUsed = stats.mLastTimeUsed;
|
||||
} else {
|
||||
stats.mLastTimeSystemUsed = statsOut.beginTime + Long.parseLong(lastTimeUsedSystem);
|
||||
}
|
||||
|
||||
final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR);
|
||||
if (!TextUtils.isEmpty(beginIdleTime)) {
|
||||
stats.mBeginIdleTime = Long.parseLong(beginIdleTime);
|
||||
}
|
||||
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
|
||||
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
|
||||
}
|
||||
@@ -141,13 +124,10 @@ final class UsageStatsXmlV1 {
|
||||
// Write the time offset.
|
||||
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
|
||||
usageStats.mLastTimeUsed - stats.beginTime);
|
||||
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_SYSTEM_ATTR,
|
||||
usageStats.mLastTimeSystemUsed - stats.beginTime);
|
||||
|
||||
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
|
||||
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
|
||||
XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
|
||||
XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime);
|
||||
|
||||
xml.endTag(null, PACKAGE_TAG);
|
||||
}
|
||||
@@ -255,7 +235,6 @@ final class UsageStatsXmlV1 {
|
||||
}
|
||||
xml.endTag(null, PACKAGES_TAG);
|
||||
|
||||
|
||||
xml.startTag(null, CONFIGURATIONS_TAG);
|
||||
final int configCount = stats.configurations.size();
|
||||
for (int i = 0; i < configCount; i++) {
|
||||
|
||||
@@ -59,7 +59,6 @@ class UserUsageStatsService {
|
||||
private final Context mContext;
|
||||
private final UsageStatsDatabase mDatabase;
|
||||
private final IntervalStats[] mCurrentStats;
|
||||
private IntervalStats mAppIdleRollingWindow;
|
||||
private boolean mStatsChanged = false;
|
||||
private final UnixCalendar mDailyExpiryDate;
|
||||
private final StatsUpdatedListener mListener;
|
||||
@@ -74,7 +73,11 @@ class UserUsageStatsService {
|
||||
interface StatsUpdatedListener {
|
||||
void onStatsUpdated();
|
||||
void onStatsReloaded();
|
||||
long getAppIdleRollingWindowDurationMillis();
|
||||
/**
|
||||
* Callback that a system update was detected
|
||||
* @param mUserId user that needs to be initialized
|
||||
*/
|
||||
void onNewUpdate(int mUserId);
|
||||
}
|
||||
|
||||
UserUsageStatsService(Context context, int userId, File usageStatsDir,
|
||||
@@ -88,7 +91,7 @@ class UserUsageStatsService {
|
||||
mUserId = userId;
|
||||
}
|
||||
|
||||
void init(final long currentTimeMillis, final long deviceUsageTime) {
|
||||
void init(final long currentTimeMillis) {
|
||||
mDatabase.init(currentTimeMillis);
|
||||
|
||||
int nullCount = 0;
|
||||
@@ -112,7 +115,7 @@ class UserUsageStatsService {
|
||||
|
||||
// By calling loadActiveStats, we will
|
||||
// generate new stats for each bucket.
|
||||
loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
|
||||
loadActiveStats(currentTimeMillis);
|
||||
} else {
|
||||
// Set up the expiry date to be one day from the latest daily stat.
|
||||
// This may actually be today and we will rollover on the first event
|
||||
@@ -136,54 +139,18 @@ class UserUsageStatsService {
|
||||
stat.updateConfigurationStats(null, stat.lastTimeSaved);
|
||||
}
|
||||
|
||||
refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
|
||||
|
||||
if (mDatabase.isNewUpdate()) {
|
||||
initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
|
||||
mDatabase.isFirstUpdate());
|
||||
notifyNewUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If any of the apps don't have a last-used entry, add one now.
|
||||
* @param currentTimeMillis the current time
|
||||
* @param firstUpdate if it is the first update, touch all installed apps, otherwise only
|
||||
* touch the system apps
|
||||
*/
|
||||
private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
|
||||
boolean firstUpdate) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<PackageInfo> packages = pm.getInstalledPackagesAsUser(0, mUserId);
|
||||
final int packageCount = packages.size();
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
final PackageInfo pi = packages.get(i);
|
||||
String packageName = pi.packageName;
|
||||
if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
|
||||
&& getBeginIdleTime(packageName) == -1) {
|
||||
for (IntervalStats stats : mCurrentStats) {
|
||||
stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
|
||||
stats.updateBeginIdleTime(packageName, deviceUsageTime);
|
||||
}
|
||||
|
||||
mAppIdleRollingWindow.update(packageName, currentTimeMillis,
|
||||
Event.SYSTEM_INTERACTION);
|
||||
mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime);
|
||||
mStatsChanged = true;
|
||||
}
|
||||
}
|
||||
// Persist the new OTA-related access stats.
|
||||
persistActiveStats();
|
||||
}
|
||||
|
||||
void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
|
||||
boolean resetBeginIdleTime) {
|
||||
void onTimeChanged(long oldTime, long newTime) {
|
||||
persistActiveStats();
|
||||
mDatabase.onTimeChanged(newTime - oldTime);
|
||||
loadActiveStats(newTime, resetBeginIdleTime);
|
||||
refreshAppIdleRollingWindow(newTime, deviceUsageTime);
|
||||
loadActiveStats(newTime);
|
||||
}
|
||||
|
||||
void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
|
||||
void reportEvent(UsageEvents.Event event) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
|
||||
+ "[" + event.mTimeStamp + "]: "
|
||||
@@ -192,7 +159,7 @@ class UserUsageStatsService {
|
||||
|
||||
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
|
||||
// Need to rollover
|
||||
rolloverStats(event.mTimeStamp, deviceUsageTime);
|
||||
rolloverStats(event.mTimeStamp);
|
||||
}
|
||||
|
||||
final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
|
||||
@@ -218,35 +185,9 @@ class UserUsageStatsService {
|
||||
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
|
||||
} else {
|
||||
stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
|
||||
stats.updateBeginIdleTime(event.mPackage, deviceUsageTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.mEventType != Event.CONFIGURATION_CHANGE) {
|
||||
mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
|
||||
mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
|
||||
}
|
||||
|
||||
notifyStatsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the beginIdleTime for each of the intervals.
|
||||
* @param beginIdleTime
|
||||
*/
|
||||
void setBeginIdleTime(String packageName, long beginIdleTime) {
|
||||
for (IntervalStats stats : mCurrentStats) {
|
||||
stats.updateBeginIdleTime(packageName, beginIdleTime);
|
||||
}
|
||||
mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
|
||||
notifyStatsChanged();
|
||||
}
|
||||
|
||||
void setSystemLastUsedTime(String packageName, long lastUsedTime) {
|
||||
for (IntervalStats stats : mCurrentStats) {
|
||||
stats.updateSystemLastUsedTime(packageName, lastUsedTime);
|
||||
}
|
||||
mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
|
||||
notifyStatsChanged();
|
||||
}
|
||||
|
||||
@@ -404,24 +345,6 @@ class UserUsageStatsService {
|
||||
return new UsageEvents(results, table);
|
||||
}
|
||||
|
||||
long getBeginIdleTime(String packageName) {
|
||||
UsageStats packageUsage;
|
||||
if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return packageUsage.getBeginIdleTime();
|
||||
}
|
||||
}
|
||||
|
||||
long getSystemLastUsedTime(String packageName) {
|
||||
UsageStats packageUsage;
|
||||
if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return packageUsage.getLastTimeSystemUsed();
|
||||
}
|
||||
}
|
||||
|
||||
void persistActiveStats() {
|
||||
if (mStatsChanged) {
|
||||
Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
|
||||
@@ -436,7 +359,7 @@ class UserUsageStatsService {
|
||||
}
|
||||
}
|
||||
|
||||
private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
|
||||
private void rolloverStats(final long currentTimeMillis) {
|
||||
final long startTime = SystemClock.elapsedRealtime();
|
||||
Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
|
||||
|
||||
@@ -463,7 +386,7 @@ class UserUsageStatsService {
|
||||
|
||||
persistActiveStats();
|
||||
mDatabase.prune(currentTimeMillis);
|
||||
loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
|
||||
loadActiveStats(currentTimeMillis);
|
||||
|
||||
final int continueCount = continuePreviousDay.size();
|
||||
for (int i = 0; i < continueCount; i++) {
|
||||
@@ -477,8 +400,6 @@ class UserUsageStatsService {
|
||||
}
|
||||
persistActiveStats();
|
||||
|
||||
refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
|
||||
|
||||
final long totalTime = SystemClock.elapsedRealtime() - startTime;
|
||||
Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
|
||||
+ " milliseconds");
|
||||
@@ -491,7 +412,11 @@ class UserUsageStatsService {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
|
||||
private void notifyNewUpdate() {
|
||||
mListener.onNewUpdate(mUserId);
|
||||
}
|
||||
|
||||
private void loadActiveStats(final long currentTimeMillis) {
|
||||
for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
|
||||
final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
|
||||
if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
|
||||
@@ -514,12 +439,6 @@ class UserUsageStatsService {
|
||||
mCurrentStats[intervalType].beginTime = currentTimeMillis;
|
||||
mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
|
||||
}
|
||||
|
||||
if (resetBeginIdleTime) {
|
||||
for (UsageStats usageStats : mCurrentStats[intervalType].packageStats.values()) {
|
||||
usageStats.mBeginIdleTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mStatsChanged = false;
|
||||
@@ -538,96 +457,28 @@ class UserUsageStatsService {
|
||||
mDailyExpiryDate.getTimeInMillis() + ")");
|
||||
}
|
||||
|
||||
private static void mergePackageStats(IntervalStats dst, IntervalStats src,
|
||||
final long deviceUsageTime) {
|
||||
dst.endTime = Math.max(dst.endTime, src.endTime);
|
||||
|
||||
final int srcPackageCount = src.packageStats.size();
|
||||
for (int i = 0; i < srcPackageCount; i++) {
|
||||
final String packageName = src.packageStats.keyAt(i);
|
||||
final UsageStats srcStats = src.packageStats.valueAt(i);
|
||||
UsageStats dstStats = dst.packageStats.get(packageName);
|
||||
if (dstStats == null) {
|
||||
dstStats = new UsageStats(srcStats);
|
||||
dst.packageStats.put(packageName, dstStats);
|
||||
} else {
|
||||
dstStats.add(src.packageStats.valueAt(i));
|
||||
}
|
||||
|
||||
// App idle times can not begin in the future. This happens if we had a time change.
|
||||
if (dstStats.mBeginIdleTime > deviceUsageTime) {
|
||||
dstStats.mBeginIdleTime = deviceUsageTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* App idle operates on a rolling window of time. When we roll over time, we end up with a
|
||||
* period of time where in-memory stats are empty and we don't hit the disk for older stats
|
||||
* for performance reasons. Suddenly all apps will become idle.
|
||||
*
|
||||
* Instead, at times we do a deep query to find all the apps that have run in the past few
|
||||
* days and keep the cached data up to date.
|
||||
*
|
||||
* @param currentTimeMillis
|
||||
*/
|
||||
void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
|
||||
// Start the rolling window for AppIdle requests.
|
||||
final long startRangeMillis = currentTimeMillis -
|
||||
mListener.getAppIdleRollingWindowDurationMillis();
|
||||
|
||||
List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
|
||||
startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() {
|
||||
@Override
|
||||
public void combine(IntervalStats stats, boolean mutable,
|
||||
List<IntervalStats> accumulatedResult) {
|
||||
IntervalStats accum;
|
||||
if (accumulatedResult.isEmpty()) {
|
||||
accum = new IntervalStats();
|
||||
accum.beginTime = stats.beginTime;
|
||||
accumulatedResult.add(accum);
|
||||
} else {
|
||||
accum = accumulatedResult.get(0);
|
||||
}
|
||||
|
||||
mergePackageStats(accum, stats, deviceUsageTime);
|
||||
}
|
||||
});
|
||||
|
||||
if (stats == null || stats.isEmpty()) {
|
||||
mAppIdleRollingWindow = new IntervalStats();
|
||||
mergePackageStats(mAppIdleRollingWindow,
|
||||
mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
|
||||
} else {
|
||||
mAppIdleRollingWindow = stats.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// -- DUMP related methods --
|
||||
//
|
||||
|
||||
void checkin(final IndentingPrintWriter pw, final long screenOnTime) {
|
||||
void checkin(final IndentingPrintWriter pw) {
|
||||
mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
|
||||
@Override
|
||||
public boolean checkin(IntervalStats stats) {
|
||||
printIntervalStats(pw, stats, screenOnTime, false);
|
||||
printIntervalStats(pw, stats, false);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void dump(IndentingPrintWriter pw, final long screenOnTime) {
|
||||
void dump(IndentingPrintWriter pw) {
|
||||
// This is not a check-in, only dump in-memory stats.
|
||||
for (int interval = 0; interval < mCurrentStats.length; interval++) {
|
||||
pw.print("In-memory ");
|
||||
pw.print(intervalToString(interval));
|
||||
pw.println(" stats");
|
||||
printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
|
||||
printIntervalStats(pw, mCurrentStats[interval], true);
|
||||
}
|
||||
|
||||
pw.println("AppIdleRollingWindow cache");
|
||||
printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
|
||||
}
|
||||
|
||||
private String formatDateTime(long dateTime, boolean pretty) {
|
||||
@@ -644,7 +495,7 @@ class UserUsageStatsService {
|
||||
return Long.toString(elapsedTime);
|
||||
}
|
||||
|
||||
void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime,
|
||||
void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
|
||||
boolean prettyDates) {
|
||||
if (prettyDates) {
|
||||
pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
|
||||
@@ -665,10 +516,6 @@ class UserUsageStatsService {
|
||||
pw.printPair("totalTime",
|
||||
formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
|
||||
pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
|
||||
pw.printPair("lastTimeSystem",
|
||||
formatDateTime(usageStats.mLastTimeSystemUsed, prettyDates));
|
||||
pw.printPair("inactiveTime",
|
||||
formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates));
|
||||
pw.println();
|
||||
}
|
||||
pw.decreaseIndent();
|
||||
|
||||
Reference in New Issue
Block a user