Accumulate network statistics based on deltas.

Network stats are now read out of the kernel in one sweep, instead of
reading per-UID.  We now accumulate the delta traffic between each
stats snapshot using the well-tested SamplingCounter pattern.

Since Wi-Fi and mobile traffic have different costs, track each
separately.  Avoids counting misc interfaces like loopback and
ethernet under total.

Bug: 5543387
Change-Id: I642004dc530113c27ef79f2abbae51d8af30117f
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 78bf9af..ffcc297 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -464,6 +464,21 @@
     }
 
     /**
+     * Checks if the given network type is backed by a Wi-Fi radio.
+     *
+     * @hide
+     */
+    public static boolean isNetworkTypeWifi(int networkType) {
+        switch (networkType) {
+            case TYPE_WIFI:
+            case TYPE_WIFI_P2P:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
      * Specifies the preferred network type.  When the device has more
      * than one type available the preferred network type will be used.
      * Note that this made sense when we only had 2 network types,
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d0f7511..32bbfbd 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -130,7 +130,7 @@
     /**
      * Bump the version on this if the checkin format changes.
      */
-    private static final int BATTERY_STATS_CHECKIN_VERSION = 6;
+    private static final int BATTERY_STATS_CHECKIN_VERSION = 7;
     
     private static final long BYTES_PER_KB = 1024;
     private static final long BYTES_PER_MB = 1048576; // 1024^2
@@ -259,17 +259,7 @@
          * {@hide}
          */
         public abstract int getUid();
-        
-        /**
-         * {@hide}
-         */
-        public abstract long getTcpBytesReceived(int which);
-        
-        /**
-         * {@hide}
-         */
-        public abstract long getTcpBytesSent(int which);
-        
+
         public abstract void noteWifiRunningLocked();
         public abstract void noteWifiStoppedLocked();
         public abstract void noteFullWifiLockAcquiredLocked();
@@ -304,11 +294,14 @@
         };
         
         public static final int NUM_USER_ACTIVITY_TYPES = 3;
-        
+
         public abstract void noteUserActivityLocked(int type);
         public abstract boolean hasUserActivity();
         public abstract int getUserActivityCount(int type, int which);
-        
+
+        public abstract boolean hasNetworkActivity();
+        public abstract long getNetworkActivityCount(int type, int which);
+
         public static abstract class Sensor {
             /*
              * FIXME: it's not correct to use this magic value because it
@@ -929,6 +922,15 @@
      */
     public abstract long getBluetoothOnTime(long batteryRealtime, int which);
     
+    public static final int NETWORK_MOBILE_RX_BYTES = 0;
+    public static final int NETWORK_MOBILE_TX_BYTES = 1;
+    public static final int NETWORK_WIFI_RX_BYTES = 2;
+    public static final int NETWORK_WIFI_TX_BYTES = 3;
+
+    public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_TX_BYTES + 1;
+
+    public abstract long getNetworkActivityCount(int type, int which);
+
     /**
      * Return whether we are currently running on battery.
      */
@@ -1246,16 +1248,20 @@
                 totalRealtime / 1000, totalUptime / 1000); 
         
         // Calculate total network and wakelock times across all uids.
-        long rxTotal = 0;
-        long txTotal = 0;
+        long mobileRxTotal = 0;
+        long mobileTxTotal = 0;
+        long wifiRxTotal = 0;
+        long wifiTxTotal = 0;
         long fullWakeLockTimeTotal = 0;
         long partialWakeLockTimeTotal = 0;
         
         for (int iu = 0; iu < NU; iu++) {
             Uid u = uidStats.valueAt(iu);
-            rxTotal += u.getTcpBytesReceived(which);
-            txTotal += u.getTcpBytesSent(which);
-            
+            mobileRxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+            mobileTxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+            wifiRxTotal += u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+            wifiTxTotal += u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
+
             Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
             if (wakelocks.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent 
@@ -1279,7 +1285,8 @@
         // Dump misc stats
         dumpLine(pw, 0 /* uid */, category, MISC_DATA,
                 screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000,
-                wifiRunningTime / 1000, bluetoothOnTime / 1000, rxTotal, txTotal, 
+                wifiRunningTime / 1000, bluetoothOnTime / 1000,
+                mobileRxTotal, mobileTxTotal, wifiRxTotal, wifiTxTotal,
                 fullWakeLockTimeTotal, partialWakeLockTimeTotal,
                 getInputEventCount(which));
         
@@ -1350,14 +1357,18 @@
             }
             Uid u = uidStats.valueAt(iu);
             // Dump Network stats per uid, if any
-            long rx = u.getTcpBytesReceived(which);
-            long tx = u.getTcpBytesSent(which);
+            long mobileRx = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+            long mobileTx = u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+            long wifiRx = u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+            long wifiTx = u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
             long fullWifiLockOnTime = u.getFullWifiLockTime(batteryRealtime, which);
             long wifiScanTime = u.getWifiScanTime(batteryRealtime, which);
             long uidWifiRunningTime = u.getWifiRunningTime(batteryRealtime, which);
-            
-            if (rx > 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx);
-            
+
+            if (mobileRx > 0 || mobileTx > 0 || wifiRx > 0 || wifiTx > 0) {
+                dumpLine(pw, uid, category, NETWORK_DATA, mobileRx, mobileTx, wifiRx, wifiTx);
+            }
+
             if (fullWifiLockOnTime != 0 || wifiScanTime != 0
                     || uidWifiRunningTime != 0) {
                 dumpLine(pw, uid, category, WIFI_DATA,
@@ -1569,8 +1580,10 @@
         pw.println(sb.toString());
         
         // Calculate total network and wakelock times across all uids.
-        long rxTotal = 0;
-        long txTotal = 0;
+        long mobileRxTotal = 0;
+        long mobileTxTotal = 0;
+        long wifiRxTotal = 0;
+        long wifiTxTotal = 0;
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
 
@@ -1623,9 +1636,11 @@
 
         for (int iu = 0; iu < NU; iu++) {
             Uid u = uidStats.valueAt(iu);
-            rxTotal += u.getTcpBytesReceived(which);
-            txTotal += u.getTcpBytesSent(which);
-            
+            mobileRxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+            mobileTxTotal += u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+            wifiRxTotal += u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+            wifiTxTotal += u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
+
             Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
             if (wakelocks.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent 
@@ -1658,8 +1673,11 @@
         }
         
         pw.print(prefix);
-                pw.print("  Total received: "); pw.print(formatBytesLocked(rxTotal));
-                pw.print(", Total sent: "); pw.println(formatBytesLocked(txTotal));
+                pw.print("  Mobile total received: "); pw.print(formatBytesLocked(mobileRxTotal));
+                pw.print(", Total sent: "); pw.println(formatBytesLocked(mobileTxTotal));
+        pw.print(prefix);
+                pw.print("  Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotal));
+                pw.print(", Total sent: "); pw.println(formatBytesLocked(wifiTxTotal));
         sb.setLength(0);
         sb.append(prefix);
                 sb.append("  Total full wakelock time: "); formatTimeMs(sb,
@@ -1801,18 +1819,25 @@
             pw.println(prefix + "  #" + uid + ":");
             boolean uidActivity = false;
             
-            long tcpReceived = u.getTcpBytesReceived(which);
-            long tcpSent = u.getTcpBytesSent(which);
+            long mobileRxBytes = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which);
+            long mobileTxBytes = u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, which);
+            long wifiRxBytes = u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, which);
+            long wifiTxBytes = u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, which);
             long fullWifiLockOnTime = u.getFullWifiLockTime(batteryRealtime, which);
             long wifiScanTime = u.getWifiScanTime(batteryRealtime, which);
             long uidWifiRunningTime = u.getWifiRunningTime(batteryRealtime, which);
-            
-            if (tcpReceived != 0 || tcpSent != 0) {
-                pw.print(prefix); pw.print("    Network: ");
-                        pw.print(formatBytesLocked(tcpReceived)); pw.print(" received, ");
-                        pw.print(formatBytesLocked(tcpSent)); pw.println(" sent");
+
+            if (mobileRxBytes > 0 || mobileTxBytes > 0) {
+                pw.print(prefix); pw.print("    Mobile network: ");
+                        pw.print(formatBytesLocked(mobileRxBytes)); pw.print(" received, ");
+                        pw.print(formatBytesLocked(mobileTxBytes)); pw.println(" sent");
             }
-            
+            if (wifiRxBytes > 0 || wifiTxBytes > 0) {
+                pw.print(prefix); pw.print("    Wi-Fi network: ");
+                        pw.print(formatBytesLocked(wifiRxBytes)); pw.print(" received, ");
+                        pw.print(formatBytesLocked(wifiTxBytes)); pw.println(" sent");
+            }
+
             if (u.hasUserActivity()) {
                 boolean hasData = false;
                 for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 823e19f..525517c 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -71,6 +71,7 @@
     void noteWifiMulticastEnabledFromSource(in WorkSource ws);
     void noteWifiMulticastDisabledFromSource(in WorkSource ws);
     void noteNetworkInterfaceType(String iface, int type);
+    void noteNetworkStatsEnabled();
     void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
     long getAwakeTimeBattery();
     long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 601d3ac4..38a8c19 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.os;
 
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
 import android.bluetooth.BluetoothDevice;
@@ -46,6 +45,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.JournaledFile;
 import com.google.android.collect.Sets;
@@ -83,7 +83,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 65 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 66 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -231,6 +231,9 @@
     final StopwatchTimer[] mPhoneDataConnectionsTimer =
             new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
 
+    final LongSamplingCounter[] mNetworkActivityCounters =
+            new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+
     boolean mWifiOn;
     StopwatchTimer mWifiOnTimer;
     int mWifiOnUid = -1;
@@ -275,12 +278,6 @@
 
     long mLastWriteTime = 0; // Milliseconds
 
-    // Mobile data transferred while on battery
-    private long[] mMobileDataTx = new long[4];
-    private long[] mMobileDataRx = new long[4];
-    private long[] mTotalDataTx = new long[4];
-    private long[] mTotalDataRx = new long[4];
-
     private long mRadioDataUptime;
     private long mRadioDataStart;
 
@@ -337,9 +334,12 @@
     private HashMap<String, Integer> mUidCache = new HashMap<String, Integer>();
 
     private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
+    private NetworkStats mLastSnapshot;
 
-    /** Network ifaces that {@link ConnectivityManager} has claimed as mobile. */
+    @GuardedBy("this")
     private HashSet<String> mMobileIfaces = Sets.newHashSet();
+    @GuardedBy("this")
+    private HashSet<String> mWifiIfaces = Sets.newHashSet();
 
     // For debugging
     public BatteryStatsImpl() {
@@ -466,7 +466,6 @@
     }
 
     public static class SamplingCounter extends Counter {
-
         SamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) {
             super(unpluggables, in);
         }
@@ -480,6 +479,93 @@
         }
     }
 
+    public static class LongSamplingCounter implements Unpluggable {
+        final ArrayList<Unpluggable> mUnpluggables;
+        long mCount;
+        long mLoadedCount;
+        long mLastCount;
+        long mUnpluggedCount;
+        long mPluggedCount;
+
+        LongSamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) {
+            mUnpluggables = unpluggables;
+            mPluggedCount = in.readLong();
+            mCount = mPluggedCount;
+            mLoadedCount = in.readLong();
+            mLastCount = 0;
+            mUnpluggedCount = in.readLong();
+            unpluggables.add(this);
+        }
+
+        LongSamplingCounter(ArrayList<Unpluggable> unpluggables) {
+            mUnpluggables = unpluggables;
+            unpluggables.add(this);
+        }
+
+        public void writeToParcel(Parcel out) {
+            out.writeLong(mCount);
+            out.writeLong(mLoadedCount);
+            out.writeLong(mUnpluggedCount);
+        }
+
+        @Override
+        public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+            mUnpluggedCount = mPluggedCount;
+            mCount = mPluggedCount;
+        }
+
+        @Override
+        public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+            mPluggedCount = mCount;
+        }
+
+        public long getCountLocked(int which) {
+            long val;
+            if (which == STATS_LAST) {
+                val = mLastCount;
+            } else {
+                val = mCount;
+                if (which == STATS_SINCE_UNPLUGGED) {
+                    val -= mUnpluggedCount;
+                } else if (which != STATS_SINCE_CHARGED) {
+                    val -= mLoadedCount;
+                }
+            }
+
+            return val;
+        }
+
+        void addCountLocked(long count) {
+            mCount += count;
+        }
+
+        /**
+         * Clear state of this counter.
+         */
+        void reset(boolean detachIfReset) {
+            mCount = 0;
+            mLoadedCount = mLastCount = mPluggedCount = mUnpluggedCount = 0;
+            if (detachIfReset) {
+                detach();
+            }
+        }
+
+        void detach() {
+            mUnpluggables.remove(this);
+        }
+
+        void writeSummaryFromParcelLocked(Parcel out) {
+            out.writeLong(mCount);
+        }
+
+        void readSummaryFromParcelLocked(Parcel in) {
+            mLoadedCount = in.readLong();
+            mCount = mLoadedCount;
+            mLastCount = 0;
+            mUnpluggedCount = mPluggedCount = mLoadedCount;
+        }
+    }
+
     /**
      * State for keeping track of timing information.
      */
@@ -1316,15 +1402,6 @@
         return kwlt;
     }
 
-    private void doDataPlug(long[] dataTransfer, long currentBytes) {
-        dataTransfer[STATS_LAST] = dataTransfer[STATS_SINCE_UNPLUGGED];
-        dataTransfer[STATS_SINCE_UNPLUGGED] = -1;
-    }
-
-    private void doDataUnplug(long[] dataTransfer, long currentBytes) {
-        dataTransfer[STATS_SINCE_UNPLUGGED] = currentBytes;
-    }
-
     /**
      * Radio uptime in microseconds when transferring data. This value is very approximate.
      * @return
@@ -1571,34 +1648,10 @@
     }
 
     public void doUnplugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
-        NetworkStats.Entry entry = null;
-
-        // Track UID data usage
-        final NetworkStats uidStats = getNetworkStatsDetailGroupedByUid();
-        final int size = uidStats.size();
-        for (int i = 0; i < size; i++) {
-            entry = uidStats.getValues(i, entry);
-
-            final Uid u = getUidStatsLocked(entry.uid);
-            u.mStartedTcpBytesReceived = entry.rxBytes;
-            u.mStartedTcpBytesSent = entry.txBytes;
-            u.mTcpBytesReceivedAtLastUnplug = u.mCurrentTcpBytesReceived;
-            u.mTcpBytesSentAtLastUnplug = u.mCurrentTcpBytesSent;
-        }
-
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
             mUnpluggables.get(i).unplug(elapsedRealtime, batteryUptime, batteryRealtime);
         }
 
-        // Track both mobile and total overall data
-        final NetworkStats ifaceStats = getNetworkStatsSummary();
-        entry = ifaceStats.getTotal(entry, mMobileIfaces);
-        doDataUnplug(mMobileDataRx, entry.rxBytes);
-        doDataUnplug(mMobileDataTx, entry.txBytes);
-        entry = ifaceStats.getTotal(entry);
-        doDataUnplug(mTotalDataRx, entry.rxBytes);
-        doDataUnplug(mTotalDataTx, entry.txBytes);
-
         // Track radio awake time
         mRadioDataStart = getCurrentRadioDataUptime();
         mRadioDataUptime = 0;
@@ -1609,32 +1662,10 @@
     }
 
     public void doPlugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
-        NetworkStats.Entry entry = null;
-
-        for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
-            Uid u = mUidStats.valueAt(iu);
-            if (u.mStartedTcpBytesReceived >= 0) {
-                u.mCurrentTcpBytesReceived = u.computeCurrentTcpBytesReceived();
-                u.mStartedTcpBytesReceived = -1;
-            }
-            if (u.mStartedTcpBytesSent >= 0) {
-                u.mCurrentTcpBytesSent = u.computeCurrentTcpBytesSent();
-                u.mStartedTcpBytesSent = -1;
-            }
-        }
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
             mUnpluggables.get(i).plug(elapsedRealtime, batteryUptime, batteryRealtime);
         }
 
-        // Track both mobile and total overall data
-        final NetworkStats ifaceStats = getNetworkStatsSummary();
-        entry = ifaceStats.getTotal(entry, mMobileIfaces);
-        doDataPlug(mMobileDataRx, entry.rxBytes);
-        doDataPlug(mMobileDataTx, entry.txBytes);
-        entry = ifaceStats.getTotal(entry);
-        doDataPlug(mTotalDataRx, entry.rxBytes);
-        doDataPlug(mTotalDataTx, entry.txBytes);
-
         // Track radio awake time
         mRadioDataUptime = getRadioDataUptime();
         mRadioDataStart = -1;
@@ -2441,6 +2472,18 @@
         } else {
             mMobileIfaces.remove(iface);
         }
+        if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+            mWifiIfaces.add(iface);
+        } else {
+            mWifiIfaces.remove(iface);
+        }
+    }
+
+    public void noteNetworkStatsEnabledLocked() {
+        // During device boot, qtaguid isn't enabled until after the inital
+        // loading of battery stats. Now that they're enabled, take our initial
+        // snapshot for future delta calculation.
+        updateNetworkActivityLocked();
     }
 
     @Override public long getScreenOnTime(long batteryRealtime, int which) {
@@ -2499,6 +2542,15 @@
         return mBluetoothOnTimer.getTotalTimeLocked(batteryRealtime, which);
     }
 
+    @Override
+    public long getNetworkActivityCount(int type, int which) {
+        if (type >= 0 && type < mNetworkActivityCounters.length) {
+            return mNetworkActivityCounters[type].getCountLocked(which);
+        } else {
+            return 0;
+        }
+    }
+
     @Override public boolean getIsOnBattery() {
         return mOnBattery;
     }
@@ -2513,17 +2565,6 @@
     public final class Uid extends BatteryStats.Uid {
 
         final int mUid;
-        long mLoadedTcpBytesReceived;
-        long mLoadedTcpBytesSent;
-        long mCurrentTcpBytesReceived;
-        long mCurrentTcpBytesSent;
-        long mTcpBytesReceivedAtLastUnplug;
-        long mTcpBytesSentAtLastUnplug;
-
-        // These are not saved/restored when parcelling, since we want
-        // to return from the parcel with a snapshot of the state.
-        long mStartedTcpBytesReceived = -1;
-        long mStartedTcpBytesSent = -1;
 
         boolean mWifiRunning;
         StopwatchTimer mWifiRunningTimer;
@@ -2549,6 +2590,8 @@
 
         Counter[] mUserActivityCounters;
 
+        LongSamplingCounter[] mNetworkActivityCounters;
+
         /**
          * The statistics we have collected for this uid's wake locks.
          */
@@ -2612,43 +2655,6 @@
         }
 
         @Override
-        public long getTcpBytesReceived(int which) {
-            if (which == STATS_LAST) {
-                return mLoadedTcpBytesReceived;
-            } else {
-                long current = computeCurrentTcpBytesReceived();
-                if (which == STATS_SINCE_UNPLUGGED) {
-                    current -= mTcpBytesReceivedAtLastUnplug;
-                } else if (which == STATS_SINCE_CHARGED) {
-                    current += mLoadedTcpBytesReceived;
-                }
-                return current;
-            }
-        }
-
-        public long computeCurrentTcpBytesReceived() {
-            final long uidRxBytes = getNetworkStatsDetailGroupedByUid().getTotal(
-                    null, mUid).rxBytes;
-            return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0
-                    ? (uidRxBytes - mStartedTcpBytesReceived) : 0);
-        }
-
-        @Override
-        public long getTcpBytesSent(int which) {
-            if (which == STATS_LAST) {
-                return mLoadedTcpBytesSent;
-            } else {
-                long current = computeCurrentTcpBytesSent();
-                if (which == STATS_SINCE_UNPLUGGED) {
-                    current -= mTcpBytesSentAtLastUnplug;
-                } else if (which == STATS_SINCE_CHARGED) {
-                    current += mLoadedTcpBytesSent;
-                }
-                return current;
-            }
-        }
-
-        @Override
         public void noteWifiRunningLocked() {
             if (!mWifiRunning) {
                 mWifiRunning = true;
@@ -2911,11 +2917,38 @@
             }
         }
 
-        public long computeCurrentTcpBytesSent() {
-            final long uidTxBytes = getNetworkStatsDetailGroupedByUid().getTotal(
-                    null, mUid).txBytes;
-            return mCurrentTcpBytesSent + (mStartedTcpBytesSent >= 0
-                    ? (uidTxBytes - mStartedTcpBytesSent) : 0);
+        void noteNetworkActivityLocked(int type, long delta) {
+            if (mNetworkActivityCounters == null) {
+                initNetworkActivityLocked();
+            }
+            if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
+                mNetworkActivityCounters[type].addCountLocked(delta);
+            } else {
+                Slog.w(TAG, "Unknown network activity type " + type + " was specified.",
+                        new Throwable());
+            }
+        }
+
+        @Override
+        public boolean hasNetworkActivity() {
+            return mNetworkActivityCounters != null;
+        }
+
+        @Override
+        public long getNetworkActivityCount(int type, int which) {
+            if (mNetworkActivityCounters != null && type >= 0
+                    && type < mNetworkActivityCounters.length) {
+                return mNetworkActivityCounters[type].getCountLocked(which);
+            } else {
+                return 0;
+            }
+        }
+
+        void initNetworkActivityLocked() {
+            mNetworkActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+            for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables);
+            }
         }
 
         /**
@@ -2961,15 +2994,18 @@
                 }
             }
 
-            mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
-            mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
-
             if (mUserActivityCounters != null) {
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i].reset(false);
                 }
             }
 
+            if (mNetworkActivityCounters != null) {
+                for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                    mNetworkActivityCounters[i].reset(false);
+                }
+            }
+
             if (mWakelockStats.size() > 0) {
                 Iterator<Map.Entry<String, Wakelock>> it = mWakelockStats.entrySet().iterator();
                 while (it.hasNext()) {
@@ -3060,6 +3096,11 @@
                         mUserActivityCounters[i].detach();
                     }
                 }
+                if (mNetworkActivityCounters != null) {
+                    for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                        mNetworkActivityCounters[i].detach();
+                    }
+                }
             }
 
             return !active;
@@ -3094,12 +3135,6 @@
                 pkg.writeToParcelLocked(out);
             }
 
-            out.writeLong(mLoadedTcpBytesReceived);
-            out.writeLong(mLoadedTcpBytesSent);
-            out.writeLong(computeCurrentTcpBytesReceived());
-            out.writeLong(computeCurrentTcpBytesSent());
-            out.writeLong(mTcpBytesReceivedAtLastUnplug);
-            out.writeLong(mTcpBytesSentAtLastUnplug);
             if (mWifiRunningTimer != null) {
                 out.writeInt(1);
                 mWifiRunningTimer.writeToParcel(out, batteryRealtime);
@@ -3156,6 +3191,14 @@
             } else {
                 out.writeInt(0);
             }
+            if (mNetworkActivityCounters != null) {
+                out.writeInt(1);
+                for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                    mNetworkActivityCounters[i].writeToParcel(out);
+                }
+            } else {
+                out.writeInt(0);
+            }
         }
 
         void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
@@ -3198,12 +3241,6 @@
                 mPackageStats.put(packageName, pkg);
             }
 
-            mLoadedTcpBytesReceived = in.readLong();
-            mLoadedTcpBytesSent = in.readLong();
-            mCurrentTcpBytesReceived = in.readLong();
-            mCurrentTcpBytesSent = in.readLong();
-            mTcpBytesReceivedAtLastUnplug = in.readLong();
-            mTcpBytesSentAtLastUnplug = in.readLong();
             mWifiRunning = false;
             if (in.readInt() != 0) {
                 mWifiRunningTimer = new StopwatchTimer(Uid.this, WIFI_RUNNING,
@@ -3266,6 +3303,14 @@
             } else {
                 mUserActivityCounters = null;
             }
+            if (in.readInt() != 0) {
+                mNetworkActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+                for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                    mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in);
+                }
+            } else {
+                mNetworkActivityCounters = null;
+            }
         }
 
         /**
@@ -4340,6 +4385,9 @@
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
             mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i, null, mUnpluggables);
         }
+        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+            mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables);
+        }
         mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables);
         mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables);
         mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables);
@@ -4515,6 +4563,9 @@
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
             mPhoneDataConnectionsTimer[i].reset(this, false);
         }
+        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+            mNetworkActivityCounters[i].reset(false);
+        }
         mWifiOnTimer.reset(this, false);
         mGlobalWifiRunningTimer.reset(this, false);
         mBluetoothOnTimer.reset(this, false);
@@ -4590,6 +4641,7 @@
                 mDischargeStartLevel = level;
             }
             updateKernelWakelocksLocked();
+            updateNetworkActivityLocked();
             mHistoryCur.batteryLevel = (byte)level;
             mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
@@ -4612,6 +4664,7 @@
             doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
         } else {
             updateKernelWakelocksLocked();
+            updateNetworkActivityLocked();
             mHistoryCur.batteryLevel = (byte)level;
             mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
@@ -4744,6 +4797,52 @@
         }
     }
 
+    private void updateNetworkActivityLocked() {
+        if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
+
+        final NetworkStats snapshot;
+        try {
+            snapshot = mNetworkStatsFactory.readNetworkStatsDetail();
+        } catch (IOException e) {
+            Log.wtf(TAG, "Failed to read network stats", e);
+            return;
+        }
+
+        if (mLastSnapshot == null) {
+            mLastSnapshot = snapshot;
+            return;
+        }
+
+        final NetworkStats delta = snapshot.subtract(mLastSnapshot);
+        mLastSnapshot = snapshot;
+
+        NetworkStats.Entry entry = null;
+        final int size = delta.size();
+        for (int i = 0; i < size; i++) {
+            entry = delta.getValues(i, entry);
+
+            if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+            if (entry.tag != NetworkStats.TAG_NONE) continue;
+
+            final Uid u = getUidStatsLocked(entry.uid);
+
+            if (mMobileIfaces.contains(entry.iface)) {
+                u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_BYTES, entry.rxBytes);
+                u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_BYTES, entry.txBytes);
+
+                mNetworkActivityCounters[NETWORK_MOBILE_RX_BYTES].addCountLocked(entry.rxBytes);
+                mNetworkActivityCounters[NETWORK_MOBILE_TX_BYTES].addCountLocked(entry.txBytes);
+
+            } else if (mWifiIfaces.contains(entry.iface)) {
+                u.noteNetworkActivityLocked(NETWORK_WIFI_RX_BYTES, entry.rxBytes);
+                u.noteNetworkActivityLocked(NETWORK_WIFI_TX_BYTES, entry.txBytes);
+
+                mNetworkActivityCounters[NETWORK_WIFI_RX_BYTES].addCountLocked(entry.rxBytes);
+                mNetworkActivityCounters[NETWORK_WIFI_TX_BYTES].addCountLocked(entry.txBytes);
+            }
+        }
+    }
+
     public long getAwakeTimeBattery() {
         return computeBatteryUptime(getBatteryUptimeLocked(), STATS_CURRENT);
     }
@@ -4834,47 +4933,6 @@
         return getBatteryRealtimeLocked(curTime);
     }
 
-    private long getTcpBytes(long current, long[] dataBytes, int which) {
-        if (which == STATS_LAST) {
-            return dataBytes[STATS_LAST];
-        } else {
-            if (which == STATS_SINCE_UNPLUGGED) {
-                if (dataBytes[STATS_SINCE_UNPLUGGED] < 0) {
-                    return dataBytes[STATS_LAST];
-                } else {
-                    return current - dataBytes[STATS_SINCE_UNPLUGGED];
-                }
-            } else if (which == STATS_SINCE_CHARGED) {
-                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_SINCE_CHARGED];
-            }
-            return current - dataBytes[STATS_CURRENT];
-        }
-    }
-
-    /** Only STATS_UNPLUGGED works properly */
-    public long getMobileTcpBytesSent(int which) {
-        final long mobileTxBytes = getNetworkStatsSummary().getTotal(null, mMobileIfaces).txBytes;
-        return getTcpBytes(mobileTxBytes, mMobileDataTx, which);
-    }
-
-    /** Only STATS_UNPLUGGED works properly */
-    public long getMobileTcpBytesReceived(int which) {
-        final long mobileRxBytes = getNetworkStatsSummary().getTotal(null, mMobileIfaces).rxBytes;
-        return getTcpBytes(mobileRxBytes, mMobileDataRx, which);
-    }
-
-    /** Only STATS_UNPLUGGED works properly */
-    public long getTotalTcpBytesSent(int which) {
-        final long totalTxBytes = getNetworkStatsSummary().getTotal(null).txBytes;
-        return getTcpBytes(totalTxBytes, mTotalDataTx, which);
-    }
-
-    /** Only STATS_UNPLUGGED works properly */
-    public long getTotalTcpBytesReceived(int which) {
-        final long totalRxBytes = getNetworkStatsSummary().getTotal(null).rxBytes;
-        return getTcpBytes(totalRxBytes, mTotalDataRx, which);
-    }
-
     @Override
     public int getDischargeStartLevel() {
         synchronized(this) {
@@ -5354,6 +5412,9 @@
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
             mPhoneDataConnectionsTimer[i].readSummaryFromParcelLocked(in);
         }
+        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+            mNetworkActivityCounters[i].readSummaryFromParcelLocked(in);
+        }
         mWifiOn = false;
         mWifiOnTimer.readSummaryFromParcelLocked(in);
         mGlobalWifiRunning = false;
@@ -5425,6 +5486,15 @@
                 }
             }
 
+            if (in.readInt() != 0) {
+                if (u.mNetworkActivityCounters == null) {
+                    u.initNetworkActivityLocked();
+                }
+                for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                    u.mNetworkActivityCounters[i].readSummaryFromParcelLocked(in);
+                }
+            }
+
             int NW = in.readInt();
             if (NW > 100) {
                 Slog.w(TAG, "File corrupt: too many wake locks " + NW);
@@ -5507,9 +5577,6 @@
                     s.mLaunches = s.mLoadedLaunches = in.readInt();
                 }
             }
-
-            u.mLoadedTcpBytesReceived = in.readLong();
-            u.mLoadedTcpBytesSent = in.readLong();
         }
     }
 
@@ -5522,6 +5589,7 @@
     public void writeSummaryToParcel(Parcel out) {
         // Need to update with current kernel wake lock counts.
         updateKernelWakelocksLocked();
+        updateNetworkActivityLocked();
 
         final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
         final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
@@ -5557,6 +5625,9 @@
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
             mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
         }
+        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+            mNetworkActivityCounters[i].writeSummaryFromParcelLocked(out);
+        }
         mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
         mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
         mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -5638,6 +5709,15 @@
                 }
             }
 
+            if (u.mNetworkActivityCounters == null) {
+                out.writeInt(0);
+            } else {
+                out.writeInt(1);
+                for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+                    u.mNetworkActivityCounters[i].writeSummaryFromParcelLocked(out);
+                }
+            }
+
             int NW = u.mWakelockStats.size();
             out.writeInt(NW);
             if (NW > 0) {
@@ -5730,9 +5810,6 @@
                     }
                 }
             }
-
-            out.writeLong(u.getTcpBytesReceived(STATS_SINCE_CHARGED));
-            out.writeLong(u.getTcpBytesSent(STATS_SINCE_CHARGED));
         }
     }
 
@@ -5771,6 +5848,9 @@
             mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i,
                     null, mUnpluggables, in);
         }
+        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+            mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in);
+        }
         mWifiOn = false;
         mWifiOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mGlobalWifiRunning = false;
@@ -5801,15 +5881,6 @@
         mDischargeAmountScreenOffSinceCharge = in.readInt();
         mLastWriteTime = in.readLong();
 
-        mMobileDataRx[STATS_LAST] = in.readLong();
-        mMobileDataRx[STATS_SINCE_UNPLUGGED] = -1;
-        mMobileDataTx[STATS_LAST] = in.readLong();
-        mMobileDataTx[STATS_SINCE_UNPLUGGED] = -1;
-        mTotalDataRx[STATS_LAST] = in.readLong();
-        mTotalDataRx[STATS_SINCE_UNPLUGGED] = -1;
-        mTotalDataTx[STATS_LAST] = in.readLong();
-        mTotalDataTx[STATS_SINCE_UNPLUGGED] = -1;
-
         mRadioDataUptime = in.readLong();
         mRadioDataStart = -1;
 
@@ -5859,6 +5930,7 @@
     void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
         // Need to update with current kernel wake lock counts.
         updateKernelWakelocksLocked();
+        updateNetworkActivityLocked();
 
         final long uSecUptime = SystemClock.uptimeMillis() * 1000;
         final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
@@ -5885,6 +5957,9 @@
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
             mPhoneDataConnectionsTimer[i].writeToParcel(out, batteryRealtime);
         }
+        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
+            mNetworkActivityCounters[i].writeToParcel(out);
+        }
         mWifiOnTimer.writeToParcel(out, batteryRealtime);
         mGlobalWifiRunningTimer.writeToParcel(out, batteryRealtime);
         mBluetoothOnTimer.writeToParcel(out, batteryRealtime);
@@ -5909,11 +5984,6 @@
         out.writeInt(mDischargeAmountScreenOffSinceCharge);
         out.writeLong(mLastWriteTime);
 
-        out.writeLong(getMobileTcpBytesReceived(STATS_SINCE_UNPLUGGED));
-        out.writeLong(getMobileTcpBytesSent(STATS_SINCE_UNPLUGGED));
-        out.writeLong(getTotalTcpBytesReceived(STATS_SINCE_UNPLUGGED));
-        out.writeLong(getTotalTcpBytesSent(STATS_SINCE_UNPLUGGED));
-
         // Write radio uptime for data
         out.writeLong(getRadioDataUptime());
 
@@ -5965,6 +6035,7 @@
     public void prepareForDumpLocked() {
         // Need to retrieve current kernel wake lock stats before printing.
         updateKernelWakelocksLocked();
+        updateNetworkActivityLocked();
     }
 
     public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly) {
@@ -5996,59 +6067,7 @@
             mGlobalWifiRunningTimer.logState(pr, "  ");
             pr.println("*** Bluetooth timer:");
             mBluetoothOnTimer.logState(pr, "  ");
-            pr.println("*** Mobile ifaces:");
-            pr.println(mMobileIfaces.toString());
         }
         super.dumpLocked(pw, isUnpluggedOnly);
     }
-
-    private NetworkStats mNetworkSummaryCache;
-    private NetworkStats mNetworkDetailCache;
-
-    private NetworkStats getNetworkStatsSummary() {
-        // NOTE: calls from BatteryStatsService already hold this lock
-        synchronized (this) {
-            if (mNetworkSummaryCache == null
-                    || mNetworkSummaryCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) {
-                mNetworkSummaryCache = null;
-
-                if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
-                    try {
-                        mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev();
-                    } catch (IOException e) {
-                        Log.wtf(TAG, "problem reading network stats", e);
-                    }
-                }
-
-                if (mNetworkSummaryCache == null) {
-                    mNetworkSummaryCache = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-                }
-            }
-            return mNetworkSummaryCache;
-        }
-    }
-
-    private NetworkStats getNetworkStatsDetailGroupedByUid() {
-        // NOTE: calls from BatteryStatsService already hold this lock
-        synchronized (this) {
-            if (mNetworkDetailCache == null
-                    || mNetworkDetailCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) {
-                mNetworkDetailCache = null;
-
-                if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
-                    try {
-                        mNetworkDetailCache = mNetworkStatsFactory
-                                .readNetworkStatsDetail().groupedByUid();
-                    } catch (IOException e) {
-                        Log.wtf(TAG, "problem reading network stats", e);
-                    }
-                }
-
-                if (mNetworkDetailCache == null) {
-                    mNetworkDetailCache = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-                }
-            }
-            return mNetworkDetailCache;
-        }
-    }
 }