NetworkStatsRecorderpublic class NetworkStatsRecorder extends Object Logic to record deltas between periodic {@link NetworkStats} snapshots into
{@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
Keeps pending changes in memory until they pass a specific threshold, in
bytes. Uses {@link FileRotator} for persistence logic.
Not inherently thread safe. |
Fields Summary |
---|
private static final String | TAG | private static final boolean | LOGD | private static final boolean | LOGV | private static final String | TAG_NETSTATS_DUMP | private static final boolean | DUMP_BEFORE_DELETEDump before deleting in {@link #recoverFromWtf()}. | private final com.android.internal.util.FileRotator | mRotator | private final android.net.NetworkStats.NonMonotonicObserver | mObserver | private final android.os.DropBoxManager | mDropBox | private final String | mCookie | private final long | mBucketDuration | private final boolean | mOnlyTags | private long | mPersistThresholdBytes | private android.net.NetworkStats | mLastSnapshot | private final NetworkStatsCollection | mPending | private final NetworkStatsCollection | mSinceBoot | private final CombiningRewriter | mPendingRewriter | private WeakReference | mComplete |
Constructors Summary |
---|
public NetworkStatsRecorder(com.android.internal.util.FileRotator rotator, android.net.NetworkStats.NonMonotonicObserver observer, android.os.DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags)
mRotator = checkNotNull(rotator, "missing FileRotator");
mObserver = checkNotNull(observer, "missing NonMonotonicObserver");
mDropBox = checkNotNull(dropBox, "missing DropBoxManager");
mCookie = cookie;
mBucketDuration = bucketDuration;
mOnlyTags = onlyTags;
mPending = new NetworkStatsCollection(bucketDuration);
mSinceBoot = new NetworkStatsCollection(bucketDuration);
mPendingRewriter = new CombiningRewriter(mPending);
|
Methods Summary |
---|
public void | dumpCheckin(java.io.PrintWriter pw, long start, long end)
// Only load and dump stats from the requested window
getOrLoadPartialLocked(start, end).dumpCheckin(pw, start, end);
| public void | dumpLocked(com.android.internal.util.IndentingPrintWriter pw, boolean fullHistory)
pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
if (fullHistory) {
pw.println("Complete history:");
getOrLoadCompleteLocked().dump(pw);
} else {
pw.println("History since boot:");
mSinceBoot.dump(pw);
}
| public void | forcePersistLocked(long currentTimeMillis)Force persisting any pending deltas.
if (mPending.isDirty()) {
if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
try {
mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
mRotator.maybeRotate(currentTimeMillis);
mPending.reset();
} catch (IOException e) {
Log.wtf(TAG, "problem persisting pending stats", e);
recoverFromWtf();
} catch (OutOfMemoryError e) {
Log.wtf(TAG, "problem persisting pending stats", e);
recoverFromWtf();
}
}
| public NetworkStatsCollection | getOrLoadCompleteLocked()Load complete history represented by {@link FileRotator}. Caches
internally as a {@link WeakReference}, and updated with future
{@link #recordSnapshotLocked(NetworkStats, Map, long)} snapshots as long
as reference is valid.
NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
if (res == null) {
res = loadLocked(Long.MIN_VALUE, Long.MAX_VALUE);
mComplete = new WeakReference<NetworkStatsCollection>(res);
}
return res;
| public NetworkStatsCollection | getOrLoadPartialLocked(long start, long end)
NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
if (res == null) {
res = loadLocked(start, end);
}
return res;
| public NetworkStats.Entry | getTotalSinceBootLocked(android.net.NetworkTemplate template)
return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
| public void | importLegacyNetworkLocked(java.io.File file)
// legacy file still exists; start empty to avoid double importing
mRotator.deleteAll();
final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
collection.readLegacyNetwork(file);
final long startMillis = collection.getStartMillis();
final long endMillis = collection.getEndMillis();
if (!collection.isEmpty()) {
// process legacy data, creating active file at starting time, then
// using end time to possibly trigger rotation.
mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
mRotator.maybeRotate(endMillis);
}
| public void | importLegacyUidLocked(java.io.File file)
// legacy file still exists; start empty to avoid double importing
mRotator.deleteAll();
final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
collection.readLegacyUid(file, mOnlyTags);
final long startMillis = collection.getStartMillis();
final long endMillis = collection.getEndMillis();
if (!collection.isEmpty()) {
// process legacy data, creating active file at starting time, then
// using end time to possibly trigger rotation.
mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
mRotator.maybeRotate(endMillis);
}
| private NetworkStatsCollection | loadLocked(long start, long end)
if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
try {
mRotator.readMatching(res, start, end);
res.recordCollection(mPending);
} catch (IOException e) {
Log.wtf(TAG, "problem completely reading network stats", e);
recoverFromWtf();
} catch (OutOfMemoryError e) {
Log.wtf(TAG, "problem completely reading network stats", e);
recoverFromWtf();
}
return res;
| public void | maybePersistLocked(long currentTimeMillis)Consider persisting any pending deltas, if they are beyond
{@link #mPersistThresholdBytes}.
final long pendingBytes = mPending.getTotalBytes();
if (pendingBytes >= mPersistThresholdBytes) {
forcePersistLocked(currentTimeMillis);
} else {
mRotator.maybeRotate(currentTimeMillis);
}
| public void | recordSnapshotLocked(android.net.NetworkStats snapshot, java.util.Map ifaceIdent, long currentTimeMillis)Record any delta that occurred since last {@link NetworkStats} snapshot,
using the given {@link Map} to identify network interfaces. First
snapshot is considered bootstrap, and is not counted as delta.
final HashSet<String> unknownIfaces = Sets.newHashSet();
// skip recording when snapshot missing
if (snapshot == null) return;
// assume first snapshot is bootstrap and don't record
if (mLastSnapshot == null) {
mLastSnapshot = snapshot;
return;
}
final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
final NetworkStats delta = NetworkStats.subtract(
snapshot, mLastSnapshot, mObserver, mCookie);
final long end = currentTimeMillis;
final long start = end - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
for (int i = 0; i < delta.size(); i++) {
entry = delta.getValues(i, entry);
final NetworkIdentitySet ident = ifaceIdent.get(entry.iface);
if (ident == null) {
unknownIfaces.add(entry.iface);
continue;
}
// skip when no delta occurred
if (entry.isEmpty()) continue;
// only record tag data when requested
if ((entry.tag == TAG_NONE) != mOnlyTags) {
mPending.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
// also record against boot stats when present
if (mSinceBoot != null) {
mSinceBoot.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
}
// also record against complete dataset when present
if (complete != null) {
complete.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
}
}
}
mLastSnapshot = snapshot;
if (LOGV && unknownIfaces.size() > 0) {
Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
}
| private void | recoverFromWtf()Recover from {@link FileRotator} failure by dumping state to
{@link DropBoxManager} and deleting contents.
if (DUMP_BEFORE_DELETE) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
mRotator.dumpAll(os);
} catch (IOException e) {
// ignore partial contents
os.reset();
} finally {
IoUtils.closeQuietly(os);
}
mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0);
}
mRotator.deleteAll();
| public void | removeUidsLocked(int[] uids)Remove the given UID from all {@link FileRotator} history, migrating it
to {@link TrafficStats#UID_REMOVED}.
try {
// Rewrite all persisted data to migrate UID stats
mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uids));
} catch (IOException e) {
Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
recoverFromWtf();
} catch (OutOfMemoryError e) {
Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
recoverFromWtf();
}
// Remove any pending stats
mPending.removeUids(uids);
mSinceBoot.removeUids(uids);
// Clear UID from current stats snapshot
if (mLastSnapshot != null) {
mLastSnapshot = mLastSnapshot.withoutUids(uids);
}
final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
if (complete != null) {
complete.removeUids(uids);
}
| public void | resetLocked()
mLastSnapshot = null;
mPending.reset();
mSinceBoot.reset();
mComplete.clear();
| public void | setPersistThreshold(long thresholdBytes)
if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
mPersistThresholdBytes = MathUtils.constrain(
thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
|
|