Merge "Usage Stats: don't block writing stats to disk." into gingerbread

This commit is contained in:
Brad Fitzpatrick
2010-08-03 16:57:47 -07:00
committed by Android (Google) Code Review

View File

@ -44,6 +44,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* This service collects the statistics associated with usage * This service collects the statistics associated with usage
@ -88,11 +91,13 @@ public final class UsageStatsService extends IUsageStats.Stub {
private boolean mIsResumed; private boolean mIsResumed;
private File mFile; private File mFile;
private String mFileLeaf; private String mFileLeaf;
//private File mBackupFile;
private long mLastWriteElapsedTime;
private File mDir; private File mDir;
private Calendar mCal;
private int mLastWriteDay; private Calendar mCal; // guarded by itself
private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
static class TimeStats { static class TimeStats {
int count; int count;
@ -241,31 +246,33 @@ public final class UsageStatsService extends IUsageStats.Stub {
mFileLeaf = getCurrentDateStr(FILE_PREFIX); mFileLeaf = getCurrentDateStr(FILE_PREFIX);
mFile = new File(mDir, mFileLeaf); mFile = new File(mDir, mFileLeaf);
readStatsFromFile(); readStatsFromFile();
mLastWriteElapsedTime = SystemClock.elapsedRealtime(); mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
// mCal was set by getCurrentDateStr(), want to use that same time. // mCal was set by getCurrentDateStr(), want to use that same time.
mLastWriteDay = mCal.get(Calendar.DAY_OF_YEAR); mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
} }
/* /*
* Utility method to convert date into string. * Utility method to convert date into string.
*/ */
private String getCurrentDateStr(String prefix) { private String getCurrentDateStr(String prefix) {
mCal.setTimeInMillis(System.currentTimeMillis());
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (prefix != null) { synchronized (mCal) {
sb.append(prefix); mCal.setTimeInMillis(System.currentTimeMillis());
if (prefix != null) {
sb.append(prefix);
}
sb.append(mCal.get(Calendar.YEAR));
int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
if (mm < 10) {
sb.append("0");
}
sb.append(mm);
int dd = mCal.get(Calendar.DAY_OF_MONTH);
if (dd < 10) {
sb.append("0");
}
sb.append(dd);
} }
sb.append(mCal.get(Calendar.YEAR));
int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
if (mm < 10) {
sb.append("0");
}
sb.append(mm);
int dd = mCal.get(Calendar.DAY_OF_MONTH);
if (dd < 10) {
sb.append("0");
}
sb.append(dd);
return sb.toString(); return sb.toString();
} }
@ -360,23 +367,56 @@ public final class UsageStatsService extends IUsageStats.Stub {
file.delete(); file.delete();
} }
} }
private void writeStatsToFile(boolean force) { /**
synchronized (mFileLock) { * Conditionally start up a disk write if it's been awhile, or the
* day has rolled over.
*
* This is called indirectly from user-facing actions (when
* 'force' is false) so it tries to be quick, without writing to
* disk directly or acquiring heavy locks.
*
* @params force do an unconditional, synchronous stats flush
* to disk on the current thread.
*/
private void writeStatsToFile(final boolean force) {
int curDay;
synchronized (mCal) {
mCal.setTimeInMillis(System.currentTimeMillis()); mCal.setTimeInMillis(System.currentTimeMillis());
final int curDay = mCal.get(Calendar.DAY_OF_YEAR); curDay = mCal.get(Calendar.DAY_OF_YEAR);
// Determine if the day changed... note that this will be wrong }
// if the year has changed but we are in the same day of year... final boolean dayChanged = curDay != mLastWriteDay.get();
// we can probably live with this.
final boolean dayChanged = curDay != mLastWriteDay; // Determine if the day changed... note that this will be wrong
long currElapsedTime = SystemClock.elapsedRealtime(); // if the year has changed but we are in the same day of year...
if (!force) { // we can probably live with this.
if (((currElapsedTime-mLastWriteElapsedTime) < FILE_WRITE_INTERVAL) && final long currElapsedTime = SystemClock.elapsedRealtime();
(!dayChanged)) {
// wait till the next update // Fast common path, without taking the often-contentious
return; // mFileLock.
} if (!force) {
if (!dayChanged &&
(currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
// wait till the next update
return;
} }
if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
new Thread("UsageStatsService_DiskWriter") {
public void run() {
try {
Slog.d(TAG, "Disk writer thread starting.");
writeStatsToFile(true);
} finally {
mUnforcedDiskWriteRunning.set(false);
Slog.d(TAG, "Disk writer thread ending.");
}
}
}.start();
}
return;
}
synchronized (mFileLock) {
// Get the most recent file // Get the most recent file
mFileLeaf = getCurrentDateStr(FILE_PREFIX); mFileLeaf = getCurrentDateStr(FILE_PREFIX);
// Copy current file to back up // Copy current file to back up
@ -395,10 +435,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
try { try {
// Write mStats to file // Write mStats to file
writeStatsFLOCK(); writeStatsFLOCK(mFile);
mLastWriteElapsedTime = currElapsedTime; mLastWriteElapsedTime.set(currElapsedTime);
if (dayChanged) { if (dayChanged) {
mLastWriteDay = curDay; mLastWriteDay.set(curDay);
// clear stats // clear stats
synchronized (mStats) { synchronized (mStats) {
mStats.clear(); mStats.clear();
@ -418,10 +458,11 @@ public final class UsageStatsService extends IUsageStats.Stub {
} }
} }
} }
Slog.d(TAG, "Dumped usage stats.");
} }
private void writeStatsFLOCK() throws IOException { private void writeStatsFLOCK(File file) throws IOException {
FileOutputStream stream = new FileOutputStream(mFile); FileOutputStream stream = new FileOutputStream(file);
try { try {
Parcel out = Parcel.obtain(); Parcel out = Parcel.obtain();
writeStatsToParcelFLOCK(out); writeStatsToParcelFLOCK(out);