FileDocCategorySizeDatePackage
NetworkStatsRecorder.javaAPI DocAndroid 5.1 API15218Thu Mar 12 22:22:42 GMT 2015com.android.server.net

NetworkStatsRecorder

public 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_DELETE
Dump 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 voiddumpCheckin(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 voiddumpLocked(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 voidforcePersistLocked(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 NetworkStatsCollectiongetOrLoadCompleteLocked()
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 NetworkStatsCollectiongetOrLoadPartialLocked(long start, long end)

        NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
        if (res == null) {
            res = loadLocked(start, end);
        }
        return res;
    
public NetworkStats.EntrygetTotalSinceBootLocked(android.net.NetworkTemplate template)

        return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
    
public voidimportLegacyNetworkLocked(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 voidimportLegacyUidLocked(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 NetworkStatsCollectionloadLocked(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 voidmaybePersistLocked(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 voidrecordSnapshotLocked(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 voidrecoverFromWtf()
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 voidremoveUidsLocked(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 voidresetLocked()

        mLastSnapshot = null;
        mPending.reset();
        mSinceBoot.reset();
        mComplete.clear();
    
public voidsetPersistThreshold(long thresholdBytes)

        if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
        mPersistThresholdBytes = MathUtils.constrain(
                thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);