Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f0b13b9 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

DO NOT MERGE: Sample atomic network stats buckets, full poll.

When sampling network stats, always use atomic buckets instead of
interpolating.  Always poll iface and UID together so we distribute
into buckets equally.  Move stale bucket trimming to just before
writing stats.

Bug: 5321340
Change-Id: I78a2226778a79c875f3668336e39ea24a7b4d5c4
parent cdd02c5d
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -441,10 +441,10 @@ public class NetworkStatsHistory implements Parcelable {
            final long curStart = bucketStart[i];
            final long curEnd = curStart + bucketDuration;

            // bucket is older than record; we're finished
            if (curEnd < start) break;
            // bucket is newer than record; keep looking
            if (curStart > end) continue;
            // bucket is older than request; we're finished
            if (curEnd <= start) break;
            // bucket is newer than request; keep looking
            if (curStart >= end) continue;

            // include full value for active buckets, otherwise only fractional
            final boolean activeBucket = curStart < now && curEnd > now;
@@ -466,7 +466,6 @@ public class NetworkStatsHistory implements Parcelable {
            if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
            if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
        }

        return entry;
    }

+0 −2
Original line number Diff line number Diff line
@@ -4031,8 +4031,6 @@ public final class Settings {
        public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history";
        /** {@hide} */
        public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history";
        /** {@hide} */
        public static final String NETSTATS_FORCE_COMPLETE_POLL = "netstats_force_complete_poll";

        /** Preferred NTP server. {@hide} */
        public static final String NTP_SERVER = "ntp_server";
+2 −2
Original line number Diff line number Diff line
@@ -142,5 +142,5 @@ option java_package com.android.server
# ---------------------------
# NetworkStatsService.java
# ---------------------------
51100 netstats_mobile_sample (iface_rx|2|2),(iface_tx|2|2),(uid_rx|2|2),(uid_tx|2|2)
51101 netstats_wifi_sample (iface_rx|2|2),(iface_tx|2|2),(uid_rx|2|2),(uid_tx|2|2)
51100 netstats_mobile_sample (iface_rx_bytes|2|2),(iface_tx_bytes|2|2),(iface_rx_pkts|2|1),(iface_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1)
51101 netstats_wifi_sample (iface_rx_bytes|2|2),(iface_tx_bytes|2|2),(iface_rx_pkts|2|1),(iface_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1)
+78 −96
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Secure.NETSTATS_FORCE_COMPLETE_POLL;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -136,15 +135,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
    private static final int MSG_PERFORM_POLL = 0x1;

    /** Flags to control detail level of poll event. */
    private static final int FLAG_POLL_NETWORK = 0x1;
    private static final int FLAG_POLL_UID = 0x2;
    private static final int FLAG_POLL_TETHER = 0x3;
    private static final int FLAG_PERSIST_NETWORK = 0x10;
    private static final int FLAG_PERSIST_UID = 0x20;
    private static final int FLAG_FORCE_PERSIST = 0x100;

    private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID | FLAG_POLL_TETHER;
    private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
    private static final int FLAG_PERSIST_FORCE = 0x100;

    private final Context mContext;
    private final INetworkManagementService mNetworkManager;
@@ -182,7 +176,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        public long getUidMaxHistory();
        public long getTagMaxHistory();
        public long getTimeCacheMaxAge();
        public boolean getForceCompletePoll();
    }

    private final Object mStatsLock = new Object();
@@ -529,7 +522,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
    @Override
    public void forceUpdate() {
        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
        performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
        performPoll(FLAG_PERSIST_ALL);
    }

    /**
@@ -561,7 +554,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        public void onReceive(Context context, Intent intent) {
            // on background handler thread, and verified CONNECTIVITY_INTERNAL
            // permission above.
            performPoll(FLAG_POLL_TETHER);
            performPoll(FLAG_PERSIST_NETWORK);
        }
    };

@@ -570,7 +563,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        public void onReceive(Context context, Intent intent) {
            // on background handler thread, and verified UPDATE_DEVICE_STATS
            // permission above.
            performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
            performPoll(FLAG_PERSIST_ALL);

            // verify that we're watching global alert
            registerGlobalAlert();
@@ -617,7 +610,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
            if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
                // kick off background poll to collect network stats; UID stats
                // are handled during normal polling interval.
                final int flags = FLAG_POLL_NETWORK | FLAG_PERSIST_NETWORK;
                final int flags = FLAG_PERSIST_NETWORK;
                mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();

                // re-arm global alert for next update
@@ -639,10 +632,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        // isn't perfect, since the kernel may already be counting traffic from
        // the updated network.

        // poll both network and UID stats, but only persist network stats,
        // since this codepath should stay fast. UID stats will be persisted
        // during next alarm poll event.
        performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_NETWORK);
        // poll, but only persist network stats to keep codepath fast. UID stats
        // will be persisted during next alarm poll event.
        performPollLocked(FLAG_PERSIST_NETWORK);

        final NetworkState[] states;
        try {
@@ -706,21 +698,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
        final long startRealtime = SystemClock.elapsedRealtime();

        boolean pollNetwork = (flags & FLAG_POLL_NETWORK) != 0;
        boolean pollUid = (flags & FLAG_POLL_UID) != 0;
        boolean pollTether = (flags & FLAG_POLL_TETHER) != 0;

        // when complete poll requested, any partial poll enables everything
        final boolean forceCompletePoll = mSettings.getForceCompletePoll();
        if (forceCompletePoll && (pollNetwork || pollUid || pollTether)) {
            pollNetwork = true;
            pollUid = true;
            pollTether = true;
        }

        final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
        final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
        final boolean forcePersist = (flags & FLAG_FORCE_PERSIST) != 0;
        final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;

        // try refreshing time source when stale
        if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
@@ -733,42 +713,37 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        final long threshold = mSettings.getPersistThreshold();

        try {
            if (pollNetwork) {
            // record network stats
            final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
            performNetworkPollLocked(networkSnapshot, currentTime);

            // persist when enough network data has occurred
            final NetworkStats persistNetworkDelta = computeStatsDelta(
                    mLastPersistNetworkSnapshot, networkSnapshot, true);
                final boolean pastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
                if (forcePersist || (persistNetwork && pastThreshold)) {
            final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
            if (persistForce || (persistNetwork && networkPastThreshold)) {
                writeNetworkStatsLocked();
                mLastPersistNetworkSnapshot = networkSnapshot;
            }
            }

            if (pollTether) {
            // record tethering stats; persisted during normal UID cycle below
            final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
            final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
                    ifacePairs);
            performTetherPollLocked(tetherSnapshot, currentTime);

                // persisted during normal UID cycle below
            }

            if (pollUid) {
            // record uid stats
            final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
            performUidPollLocked(uidSnapshot, currentTime);

            // persist when enough network data has occurred
            final NetworkStats persistUidDelta = computeStatsDelta(
                    mLastPersistUidSnapshot, uidSnapshot, true);
                final boolean pastThreshold = persistUidDelta.getTotalBytes() > threshold;
                if (forcePersist || (persistUid && pastThreshold)) {
            final boolean uidPastThreshold = persistUidDelta.getTotalBytes() > threshold;
            if (persistForce || (persistUid && uidPastThreshold)) {
                writeUidStatsLocked();
                mLastPersistUidSnapshot = uidSnapshot;
            }
            }
        } catch (IllegalStateException e) {
            Log.wtf(TAG, "problem reading network stats", e);
        } catch (RemoteException e) {
@@ -781,9 +756,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        }

        // sample stats after each full poll
        if (pollNetwork && pollUid) {
        performSample();
        }

        // finally, dispatch updated event to any listeners
        final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
@@ -813,12 +786,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
            history.recordData(timeStart, currentTime, entry);
        }

        // trim any history beyond max
        final long maxHistory = mSettings.getNetworkMaxHistory();
        for (NetworkStatsHistory history : mNetworkStats.values()) {
            history.removeBucketsBefore(currentTime - maxHistory);
        }

        mLastPollNetworkSnapshot = networkSnapshot;

        if (LOGD && unknownIface.size() > 0) {
@@ -862,20 +829,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
            history.recordData(timeStart, currentTime, entry);
        }

        // trim any history beyond max
        final long maxUidHistory = mSettings.getUidMaxHistory();
        final long maxTagHistory = mSettings.getTagMaxHistory();
        for (UidStatsKey key : mUidStats.keySet()) {
            final NetworkStatsHistory history = mUidStats.get(key);

            // detailed tags are trimmed sooner than summary in TAG_NONE
            if (key.tag == TAG_NONE) {
                history.removeBucketsBefore(currentTime - maxUidHistory);
            } else {
                history.removeBucketsBefore(currentTime - maxTagHistory);
            }
        }

        mLastPollUidSnapshot = uidSnapshot;
        mLastPollOperationsSnapshot = mOperations;
        mOperations = new NetworkStats(0L, 10);
@@ -917,9 +870,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
     * Sample recent statistics summary into {@link EventLog}.
     */
    private void performSample() {
        // take sample as total over last 4 hours
        final long end = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
        final long start = end - (4 * HOUR_IN_MILLIS);
        final long largestBucketSize = Math.max(
                mSettings.getNetworkBucketDuration(), mSettings.getUidBucketDuration());

        // take sample as atomic buckets
        final long now = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
        final long end = now - (now % largestBucketSize) + largestBucketSize;
        final long start = end - largestBucketSize;

        NetworkTemplate template = null;
        NetworkStats.Entry ifaceTotal = null;
@@ -929,15 +886,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
        ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
        uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
        EventLogTags.writeNetstatsMobileSample(
                ifaceTotal.rxBytes, ifaceTotal.txBytes, uidTotal.rxBytes, uidTotal.txBytes);
        EventLogTags.writeNetstatsMobileSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
                ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
                uidTotal.txBytes, uidTotal.rxPackets);

        // collect wifi sample
        template = buildTemplateWifi();
        ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
        uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
        EventLogTags.writeNetstatsWifiSample(
                ifaceTotal.rxBytes, ifaceTotal.txBytes, uidTotal.rxBytes, uidTotal.txBytes);
        EventLogTags.writeNetstatsWifiSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
                ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
                uidTotal.txBytes, uidTotal.rxPackets);
    }

    /**
@@ -1137,6 +1096,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {

        // TODO: consider duplicating stats and releasing lock while writing

        // trim any history beyond max
        if (mTime.hasCache()) {
            final long currentTime = mTime.currentTimeMillis();
            final long maxHistory = mSettings.getNetworkMaxHistory();
            for (NetworkStatsHistory history : mNetworkStats.values()) {
                history.removeBucketsBefore(currentTime - maxHistory);
            }
        }

        FileOutputStream fos = null;
        try {
            fos = mNetworkFile.startWrite();
@@ -1172,6 +1140,23 @@ public class NetworkStatsService extends INetworkStatsService.Stub {

        // TODO: consider duplicating stats and releasing lock while writing

        // trim any history beyond max
        if (mTime.hasCache()) {
            final long currentTime = mTime.currentTimeMillis();
            final long maxUidHistory = mSettings.getUidMaxHistory();
            final long maxTagHistory = mSettings.getTagMaxHistory();
            for (UidStatsKey key : mUidStats.keySet()) {
                final NetworkStatsHistory history = mUidStats.get(key);

                // detailed tags are trimmed sooner than summary in TAG_NONE
                if (key.tag == TAG_NONE) {
                    history.removeBucketsBefore(currentTime - maxUidHistory);
                } else {
                    history.removeBucketsBefore(currentTime - maxTagHistory);
                }
            }
        }

        // build UidStatsKey lists grouped by ident
        final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
        for (UidStatsKey key : mUidStats.keySet()) {
@@ -1236,7 +1221,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
            }

            if (argSet.contains("poll")) {
                performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_ALL | FLAG_FORCE_PERSIST);
                performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
                pw.println("Forced poll");
                return;
            }
@@ -1464,8 +1449,5 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        public long getTimeCacheMaxAge() {
            return DAY_IN_MILLIS;
        }
        public boolean getForceCompletePoll() {
            return getSecureBoolean(NETSTATS_FORCE_COMPLETE_POLL, false);
        }
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -272,7 +272,11 @@ public class NetworkStatsServiceTest extends AndroidTestCase {

        // graceful shutdown system, which should trigger persist of stats, and
        // clear any values in memory.
        expectCurrentTime();
        expectDefaultSettings();
        replay();
        mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
        verifyAndReset();

        // talk with zombie service to assert stats have gone; and assert that
        // we persisted them to file.
@@ -487,6 +491,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase {

        // now pretend two UIDs are uninstalled, which should migrate stats to
        // special "removed" bucket.
        expectCurrentTime();
        expectDefaultSettings();
        replay();
        final Intent intent = new Intent(ACTION_UID_REMOVED);
@@ -758,7 +763,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
        expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
        expect(mSettings.getTagMaxHistory()).andReturn(maxHistory).anyTimes();
        expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
        expect(mSettings.getForceCompletePoll()).andReturn(false).anyTimes();
    }

    private void expectCurrentTime() throws Exception {