Merge "Always splice historical data stats, debug info."
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 5929cfb..88abf1a 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -43,6 +43,9 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.AsyncChannel;
 
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+
 /**
  * Track the state of mobile data connectivity. This is done by
  * receiving broadcast intents from the Phone process whenever
@@ -69,6 +72,11 @@
     private boolean mPrivateDnsRouteSet = false;
     private boolean mDefaultRouteSet = false;
 
+    // NOTE: these are only kept for debugging output; actual values are
+    // maintained in DataConnectionTracker.
+    protected boolean mUserDataEnabled = true;
+    protected boolean mPolicyDataEnabled = true;
+
     private Handler mHandler;
     private AsyncChannel mDataConnectionTrackerAc;
     private Messenger mMessenger;
@@ -458,6 +466,7 @@
         final AsyncChannel channel = mDataConnectionTrackerAc;
         if (channel != null) {
             channel.sendMessage(CMD_SET_USER_DATA_ENABLE, enabled ? ENABLED : DISABLED);
+            mUserDataEnabled = enabled;
         }
         if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
     }
@@ -468,6 +477,7 @@
         final AsyncChannel channel = mDataConnectionTrackerAc;
         if (channel != null) {
             channel.sendMessage(CMD_SET_POLICY_DATA_ENABLE, enabled ? ENABLED : DISABLED);
+            mPolicyDataEnabled = enabled;
         }
     }
 
@@ -492,10 +502,12 @@
 
     @Override
     public String toString() {
-        StringBuffer sb = new StringBuffer("Mobile data state: ");
-
-        sb.append(mMobileDataState);
-        return sb.toString();
+        final CharArrayWriter writer = new CharArrayWriter();
+        final PrintWriter pw = new PrintWriter(writer);
+        pw.print("Mobile data state: "); pw.println(mMobileDataState);
+        pw.print("Data enabled: user="); pw.print(mUserDataEnabled);
+        pw.print(", policy="); pw.println(mPolicyDataEnabled);
+        return writer.toString();
     }
 
    /**
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 5b883a0..e5f3273 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.util.Log;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.util.Objects;
@@ -38,6 +39,8 @@
  * @hide
  */
 public class NetworkStats implements Parcelable {
+    private static final String TAG = "NetworkStats";
+
     /** {@link #iface} value when interface details unavailable. */
     public static final String IFACE_ALL = null;
     /** {@link #uid} value when UID details unavailable. */
@@ -397,7 +400,10 @@
                 if (enforceMonotonic
                         && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
                                 || entry.txPackets < 0 || entry.operations < 0)) {
-                    throw new IllegalArgumentException("found non-monotonic values");
+                    Log.v(TAG, "lhs=" + this);
+                    Log.v(TAG, "rhs=" + value);
+                    throw new IllegalArgumentException(
+                            "found non-monotonic values at lhs[" + i + "] - rhs[" + j + "]");
                 }
                 if (clampNegative) {
                     entry.rxBytes = Math.max(0, entry.rxBytes);
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b19949e..d07d899 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -482,12 +482,12 @@
         for (int i = start; i < bucketCount; i++) {
             pw.print(prefix);
             pw.print("  bucketStart="); pw.print(bucketStart[i]);
-            if (activeTime != null) pw.print(" activeTime="); pw.print(activeTime[i]);
-            if (rxBytes != null) pw.print(" rxBytes="); pw.print(rxBytes[i]);
-            if (rxPackets != null) pw.print(" rxPackets="); pw.print(rxPackets[i]);
-            if (txBytes != null) pw.print(" txBytes="); pw.print(txBytes[i]);
-            if (txPackets != null) pw.print(" txPackets="); pw.print(txPackets[i]);
-            if (operations != null) pw.print(" operations="); pw.print(operations[i]);
+            if (activeTime != null) { pw.print(" activeTime="); pw.print(activeTime[i]); }
+            if (rxBytes != null) { pw.print(" rxBytes="); pw.print(rxBytes[i]); }
+            if (rxPackets != null) { pw.print(" rxPackets="); pw.print(rxPackets[i]); }
+            if (txBytes != null) { pw.print(" txBytes="); pw.print(txBytes[i]); }
+            if (txPackets != null) { pw.print(" txPackets="); pw.print(txPackets[i]); }
+            if (operations != null) { pw.print(" operations="); pw.print(operations[i]); }
             pw.println();
         }
     }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index e72d09f..85d8cece 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
@@ -51,10 +52,12 @@
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.PrintWriter;
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -288,7 +291,6 @@
         for (INetworkManagementEventObserver obs : mObservers) {
             try {
                 obs.limitReached(limitName, iface);
-                Slog.d(TAG, "Observer notified limit reached for " + limitName + " " + iface);
             } catch (Exception ex) {
                 Slog.w(TAG, "Observer notifier failed", ex);
             }
@@ -1031,7 +1033,6 @@
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
-        final HashSet<String> activeIfaces = Sets.newHashSet();
         final ArrayList<String> values = Lists.newArrayList();
 
         BufferedReader reader = null;
@@ -1057,7 +1058,6 @@
                     entry.txBytes = Long.parseLong(values.get(9));
                     entry.txPackets = Long.parseLong(values.get(10));
 
-                    activeIfaces.add(entry.iface);
                     stats.addValues(entry);
                 } catch (NumberFormatException e) {
                     Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
@@ -1073,14 +1073,9 @@
             IoUtils.closeQuietly(reader);
         }
 
-        if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces);
-
-        // splice in stats from any disabled ifaces
+        // splice in historical stats not reflected in mStatsIface
         if (mBandwidthControlEnabled) {
-            final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface));
-            xtIfaces.removeAll(activeIfaces);
-
-            for (String iface : xtIfaces) {
+            for (String iface : fileListWithoutNull(mStatsXtIface)) {
                 final File ifacePath = new File(mStatsXtIface, iface);
 
                 entry.iface = iface;
@@ -1092,10 +1087,8 @@
                 entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes"));
                 entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets"));
 
-                stats.addValues(entry);
+                stats.combineValues(entry);
             }
-
-            if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces);
         }
 
         return stats;
@@ -1583,4 +1576,26 @@
             mConnector.monitor();
         }
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+        pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
+
+        synchronized (mQuotaLock) {
+            pw.print("Active quota ifaces: "); pw.println(mActiveQuotaIfaces.toString());
+            pw.print("Active alert ifaces: "); pw.println(mActiveAlertIfaces.toString());
+        }
+
+        synchronized (mUidRejectOnQuota) {
+            pw.print("UID reject on quota ifaces: [");
+            final int size = mUidRejectOnQuota.size();
+            for (int i = 0; i < size; i++) {
+                pw.print(mUidRejectOnQuota.keyAt(i));
+                if (i < size - 1) pw.print(",");
+            }
+            pw.println("]");
+        }
+    }
 }
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 84e5eae..77f53c2 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -133,6 +133,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 
 import libcore.io.IoUtils;
 
@@ -766,9 +767,6 @@
                     && totalBytes > policy.limitBytes && policy.lastSnooze < start;
             final boolean enabled = !overLimit;
 
-            if (LOGD) {
-                Slog.d(TAG, "setting template=" + policy.template + " enabled=" + enabled);
-            }
             setNetworkTemplateEnabled(policy.template, enabled);
         }
     }
@@ -835,9 +833,10 @@
 
             // collect all active ifaces that match this template
             ifaceList.clear();
-            for (NetworkIdentity ident : networks.keySet()) {
+            for (Map.Entry<NetworkIdentity, String> entry : networks.entrySet()) {
+                final NetworkIdentity ident = entry.getKey();
                 if (policy.template.matches(ident)) {
-                    final String iface = networks.get(ident);
+                    final String iface = entry.getValue();
                     ifaceList.add(iface);
                 }
             }
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 80ae9bc..bb0a0d1 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -707,7 +707,7 @@
                     mLastPersistUidSnapshot, uidSnapshot, true);
             if (forcePersist || persistUidDelta.getTotalBytes() > persistThreshold) {
                 writeUidStatsLocked();
-                mLastPersistNetworkSnapshot = networkSnapshot;
+                mLastPersistUidSnapshot = networkSnapshot;
             }
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index 5f35697..ecf78d9 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -117,6 +117,18 @@
         assertStatsEntry(stats, "wlan0", UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 2048L);
     }
 
+    public void testNetworkStatsCombined() throws Exception {
+        stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
+        stageLong(10L, new File(mTestProc, "net/xt_qtaguid/iface_stat/rmnet0/rx_bytes"));
+        stageLong(20L, new File(mTestProc, "net/xt_qtaguid/iface_stat/rmnet0/rx_packets"));
+        stageLong(30L, new File(mTestProc, "net/xt_qtaguid/iface_stat/rmnet0/tx_bytes"));
+        stageLong(40L, new File(mTestProc, "net/xt_qtaguid/iface_stat/rmnet0/tx_packets"));
+
+        final NetworkStats stats = mService.getNetworkStatsSummary();
+        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_DEFAULT, TAG_NONE, 1507570L + 10L,
+                2205L + 20L, 489339L + 30L, 2237L + 40L);
+    }
+
     public void testKernelTags() throws Exception {
         assertEquals("0", tagToKernel(0x0));
         assertEquals("214748364800", tagToKernel(0x32));