Begin collecting xtables iface counters.

Add method to parse new iface_stat_fmt proc stats, or return null
when kernel support is unavailable. Add test and remove older, unused
parsing code. Create new "xt" recorder to persist the new xtables
counters when available.

Add SSID support to NetworkIdentity to fix policy tests.

Bug: 6422414
Change-Id: I77f70e9acb79a559ab626f3af5c4f3599801ed43
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 4ac5e76..3c67bf9 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -158,9 +158,14 @@
             }
 
         } else if (type == TYPE_WIFI) {
-            final WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-            final WifiInfo info = wifi.getConnectionInfo();
-            networkId = info != null ? info.getSSID() : null;
+            if (state.networkId != null) {
+                networkId = state.networkId;
+            } else {
+                final WifiManager wifi = (WifiManager) context.getSystemService(
+                        Context.WIFI_SERVICE);
+                final WifiInfo info = wifi.getConnectionInfo();
+                networkId = info != null ? info.getSSID() : null;
+            }
         }
 
         return new NetworkIdentity(type, subType, subscriberId, networkId, roaming);
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 2fc69ad..fbe1f82 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -31,18 +31,20 @@
     public final LinkCapabilities linkCapabilities;
     /** Currently only used by testing. */
     public final String subscriberId;
+    public final String networkId;
 
     public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
             LinkCapabilities linkCapabilities) {
-        this(networkInfo, linkProperties, linkCapabilities, null);
+        this(networkInfo, linkProperties, linkCapabilities, null, null);
     }
 
     public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
-            LinkCapabilities linkCapabilities, String subscriberId) {
+            LinkCapabilities linkCapabilities, String subscriberId, String networkId) {
         this.networkInfo = networkInfo;
         this.linkProperties = linkProperties;
         this.linkCapabilities = linkCapabilities;
         this.subscriberId = subscriberId;
+        this.networkId = networkId;
     }
 
     public NetworkState(Parcel in) {
@@ -50,6 +52,7 @@
         linkProperties = in.readParcelable(null);
         linkCapabilities = in.readParcelable(null);
         subscriberId = in.readString();
+        networkId = in.readString();
     }
 
     @Override
@@ -63,6 +66,7 @@
         out.writeParcelable(linkProperties, flags);
         out.writeParcelable(linkCapabilities, flags);
         out.writeString(subscriberId);
+        out.writeString(networkId);
     }
 
     public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 6ecc640..e7ea355 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -238,7 +238,8 @@
      * Return global network statistics summarized at an interface level,
      * without any UID-level granularity.
      */
-    NetworkStats getNetworkStatsSummary();
+    NetworkStats getNetworkStatsSummaryDev();
+    NetworkStats getNetworkStatsSummaryXt();
 
     /**
      * Return detailed network statistics with UID-level granularity,
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index d59585f..8b222f0 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -24,20 +24,12 @@
 import android.net.NetworkStats;
 import android.os.StrictMode;
 import android.os.SystemClock;
-import android.util.Slog;
 
 import com.android.internal.util.ProcFileReader;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.StringTokenizer;
 
 import libcore.io.IoUtils;
 
@@ -50,14 +42,10 @@
 
     // TODO: consider moving parsing to native code
 
-    /** Path to {@code /proc/net/dev}. */
-    @Deprecated
-    private final File mStatsIface;
-    /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */
-    @Deprecated
-    private final File mStatsXtIface;
     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
     private final File mStatsXtIfaceAll;
+    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
+    private final File mStatsXtIfaceFmt;
     /** Path to {@code /proc/net/xt_qtaguid/stats}. */
     private final File mStatsXtUid;
 
@@ -67,28 +55,20 @@
 
     // @VisibleForTesting
     public NetworkStatsFactory(File procRoot) {
-        mStatsIface = new File(procRoot, "net/dev");
-        mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
-        mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat");
         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
+        mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
+        mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
     }
 
     /**
-     * Parse and return interface-level summary {@link NetworkStats}. Values
-     * monotonically increase since device boot, and may include details about
-     * inactive interfaces.
+     * Parse and return interface-level summary {@link NetworkStats} measured
+     * using {@code /proc/net/dev} style hooks, which may include non IP layer
+     * traffic. Values monotonically increase since device boot, and may include
+     * details about inactive interfaces.
      *
      * @throws IllegalStateException when problem parsing stats.
      */
-    public NetworkStats readNetworkStatsSummary() throws IllegalStateException {
-        if (mStatsXtIfaceAll.exists()) {
-            return readNetworkStatsSummarySingleFile();
-        } else {
-            return readNetworkStatsSummaryMultipleFiles();
-        }
-    }
-
-    private NetworkStats readNetworkStatsSummarySingleFile() {
+    public NetworkStats readNetworkStatsSummaryDev() throws IllegalStateException {
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -137,79 +117,40 @@
     }
 
     /**
-     * @deprecated remove once {@code iface_stat_all} is merged to all kernels.
+     * Parse and return interface-level summary {@link NetworkStats}. Designed
+     * to return only IP layer traffic. Values monotonically increase since
+     * device boot, and may include details about inactive interfaces.
+     *
+     * @throws IllegalStateException when problem parsing stats.
      */
-    @Deprecated
-    private NetworkStats readNetworkStatsSummaryMultipleFiles() {
+    public NetworkStats readNetworkStatsSummaryXt() throws IllegalStateException {
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
+        // return null when kernel doesn't support
+        if (!mStatsXtIfaceFmt.exists()) return null;
+
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
-        final HashSet<String> knownIfaces = Sets.newHashSet();
-        final HashSet<String> activeIfaces = Sets.newHashSet();
-
-        // collect any historical stats and active state
-        for (String iface : fileListWithoutNull(mStatsXtIface)) {
-            final File ifacePath = new File(mStatsXtIface, iface);
-
-            final long active = readSingleLongFromFile(new File(ifacePath, "active"));
-            if (active == 1) {
-                knownIfaces.add(iface);
-                activeIfaces.add(iface);
-            } else if (active == 0) {
-                knownIfaces.add(iface);
-            } else {
-                continue;
-            }
-
-            entry.iface = iface;
-            entry.uid = UID_ALL;
-            entry.set = SET_ALL;
-            entry.tag = TAG_NONE;
-            entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes"));
-            entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets"));
-            entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes"));
-            entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets"));
-
-            stats.addValues(entry);
-        }
-
-        final ArrayList<String> values = Lists.newArrayList();
-
-        BufferedReader reader = null;
+        ProcFileReader reader = null;
         try {
-            reader = new BufferedReader(new FileReader(mStatsIface));
+            // open and consume header line
+            reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
+            reader.finishLine();
 
-            // skip first two header lines
-            reader.readLine();
-            reader.readLine();
+            while (reader.hasMoreData()) {
+                entry.iface = reader.nextString();
+                entry.uid = UID_ALL;
+                entry.set = SET_ALL;
+                entry.tag = TAG_NONE;
 
-            // parse remaining lines
-            String line;
-            while ((line = reader.readLine()) != null) {
-                splitLine(line, values);
+                entry.rxBytes = reader.nextLong();
+                entry.rxPackets = reader.nextLong();
+                entry.txBytes = reader.nextLong();
+                entry.txPackets = reader.nextLong();
 
-                try {
-                    entry.iface = values.get(0);
-                    entry.uid = UID_ALL;
-                    entry.set = SET_ALL;
-                    entry.tag = TAG_NONE;
-                    entry.rxBytes = Long.parseLong(values.get(1));
-                    entry.rxPackets = Long.parseLong(values.get(2));
-                    entry.txBytes = Long.parseLong(values.get(9));
-                    entry.txPackets = Long.parseLong(values.get(10));
-
-                    if (activeIfaces.contains(entry.iface)) {
-                        // combine stats when iface is active
-                        stats.combineValues(entry);
-                    } else if (!knownIfaces.contains(entry.iface)) {
-                        // add stats when iface is unknown
-                        stats.addValues(entry);
-                    }
-                } catch (NumberFormatException e) {
-                    Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
-                }
+                stats.addValues(entry);
+                reader.finishLine();
             }
         } catch (NullPointerException e) {
             throw new IllegalStateException("problem parsing stats: " + e);
@@ -221,7 +162,6 @@
             IoUtils.closeQuietly(reader);
             StrictMode.setThreadPolicy(savedPolicy);
         }
-
         return stats;
     }
 
@@ -286,41 +226,4 @@
 
         return stats;
     }
-
-    /**
-     * Split given line into {@link ArrayList}.
-     */
-    @Deprecated
-    private static void splitLine(String line, ArrayList<String> outSplit) {
-        outSplit.clear();
-
-        final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:");
-        while (t.hasMoreTokens()) {
-            outSplit.add(t.nextToken());
-        }
-    }
-
-    /**
-     * Utility method to read a single plain-text {@link Long} from the given
-     * {@link File}, usually from a {@code /proc/} filesystem.
-     */
-    private static long readSingleLongFromFile(File file) {
-        try {
-            final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
-            return Long.parseLong(new String(buffer).trim());
-        } catch (NumberFormatException e) {
-            return -1;
-        } catch (IOException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * Wrapper for {@link File#list()} that returns empty array instead of
-     * {@code null}.
-     */
-    private static String[] fileListWithoutNull(File file) {
-        final String[] list = file.list();
-        return list != null ? list : new String[0];
-    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 86118b1..5157385 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5719,7 +5719,7 @@
 
                 if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
                     try {
-                        mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummary();
+                        mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev();
                     } catch (IllegalStateException e) {
                         Log.wtf(TAG, "problem reading network stats", e);
                     }
diff --git a/core/tests/coretests/res/raw/net_dev_typical b/core/tests/coretests/res/raw/net_dev_typical
deleted file mode 100644
index 290bf03..0000000
--- a/core/tests/coretests/res/raw/net_dev_typical
+++ /dev/null
@@ -1,8 +0,0 @@
-Inter-|   Receive                                                |  Transmit
- face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
-    lo:    8308     116    0    0    0     0          0         0     8308     116    0    0    0     0       0          0
-rmnet0: 1507570    2205    0    0    0     0          0         0   489339    2237    0    0    0     0       0          0
-  ifb0:   52454     151    0  151    0     0          0         0        0       0    0    0    0     0       0          0
-  ifb1:   52454     151    0  151    0     0          0         0        0       0    0    0    0     0       0          0
-  sit0:       0       0    0    0    0     0          0         0        0       0  148    0    0     0       0          0
-ip6tnl0:       0       0    0    0    0     0          0         0        0       0  151  151    0     0       0          0
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_iface_fmt_typical b/core/tests/coretests/res/raw/xt_qtaguid_iface_fmt_typical
new file mode 100644
index 0000000..656d5bb
--- /dev/null
+++ b/core/tests/coretests/res/raw/xt_qtaguid_iface_fmt_typical
@@ -0,0 +1,4 @@
+ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets
+rmnet2 4968 35 3081 39
+rmnet1 11153922 8051 190226 2468
+rmnet0 6824 16 5692 10
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
index 58269a8..b716d8a 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -80,58 +80,6 @@
         assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L);
     }
 
-    public void testNetworkStatsSummary() throws Exception {
-        stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
-
-        final NetworkStats stats = mFactory.readNetworkStatsSummary();
-        assertEquals(6, stats.size());
-        assertStatsEntry(stats, "lo", UID_ALL, SET_ALL, TAG_NONE, 8308L, 8308L);
-        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 1507570L, 489339L);
-        assertStatsEntry(stats, "ifb0", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
-        assertStatsEntry(stats, "ifb1", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
-        assertStatsEntry(stats, "sit0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
-        assertStatsEntry(stats, "ip6tnl0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
-    }
-
-    public void testNetworkStatsSummaryDown() throws Exception {
-        stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
-        stageLong(1L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/active"));
-        stageLong(1024L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_bytes"));
-        stageLong(128L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_packets"));
-        stageLong(2048L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_bytes"));
-        stageLong(256L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_packets"));
-
-        final NetworkStats stats = mFactory.readNetworkStatsSummary();
-        assertEquals(7, stats.size());
-        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 1507570L, 489339L);
-        assertStatsEntry(stats, "wlan0", UID_ALL, SET_ALL, TAG_NONE, 1024L, 2048L);
-    }
-
-    public void testNetworkStatsCombined() throws Exception {
-        stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
-        stageLong(1L, new File(mTestProc, "net/xt_qtaguid/iface_stat/rmnet0/active"));
-        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 = mFactory.readNetworkStatsSummary();
-        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 1507570L + 10L,
-                2205L + 20L, 489339L + 30L, 2237L + 40L);
-    }
-
-    public void testNetworkStatsCombinedInactive() throws Exception {
-        stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
-        stageLong(0L, new File(mTestProc, "net/xt_qtaguid/iface_stat/rmnet0/active"));
-        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 = mFactory.readNetworkStatsSummary();
-        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 10L, 20L, 30L, 40L);
-    }
-
     public void testKernelTags() throws Exception {
         assertEquals(0, kernelToTag("0x0000000000000000"));
         assertEquals(0x32, kernelToTag("0x0000003200000000"));
@@ -152,13 +100,24 @@
     public void testNetworkStatsSingle() throws Exception {
         stageFile(R.raw.xt_qtaguid_iface_typical, new File(mTestProc, "net/xt_qtaguid/iface_stat_all"));
 
-        final NetworkStats stats = mFactory.readNetworkStatsSummary();
+        final NetworkStats stats = mFactory.readNetworkStatsSummaryDev();
         assertEquals(6, stats.size());
         assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 2112L, 24L, 700L, 10L);
         assertStatsEntry(stats, "test1", UID_ALL, SET_ALL, TAG_NONE, 6L, 8L, 10L, 12L);
         assertStatsEntry(stats, "test2", UID_ALL, SET_ALL, TAG_NONE, 1L, 2L, 3L, 4L);
     }
 
+    public void testNetworkStatsXt() throws Exception {
+        stageFile(R.raw.xt_qtaguid_iface_fmt_typical,
+                new File(mTestProc, "net/xt_qtaguid/iface_stat_fmt"));
+
+        final NetworkStats stats = mFactory.readNetworkStatsSummaryXt();
+        assertEquals(3, stats.size());
+        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 6824L, 16L, 5692L, 10L);
+        assertStatsEntry(stats, "rmnet1", UID_ALL, SET_ALL, TAG_NONE, 11153922L, 8051L, 190226L, 2468L);
+        assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L);
+    }
+
     /**
      * Copy a {@link Resources#openRawResource(int)} into {@link File} for
      * testing purposes.
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 09d0698..82a7d51 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -942,9 +942,15 @@
     }
 
     @Override
-    public NetworkStats getNetworkStatsSummary() {
+    public NetworkStats getNetworkStatsSummaryDev() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        return mStatsFactory.readNetworkStatsSummary();
+        return mStatsFactory.readNetworkStatsSummaryDev();
+    }
+
+    @Override
+    public NetworkStats getNetworkStatsSummaryXt() {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        return mStatsFactory.readNetworkStatsSummaryXt();
     }
 
     @Override
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 2155147..f35a5af 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -511,7 +511,7 @@
             long incRead = 0;
             long incWrite = 0;
             try {
-                final NetworkStats stats = mNMService.getNetworkStatsSummary();
+                final NetworkStats stats = mNMService.getNetworkStatsSummaryDev();
                 final int index = stats.findIndex(mIface, NetworkStats.UID_ALL,
                         NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE);
 
diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java
index 57ad158..743a746 100644
--- a/services/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/java/com/android/server/net/NetworkStatsRecorder.java
@@ -128,6 +128,9 @@
             Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
         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;
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 1c3e24f..6d6f59c 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -159,6 +159,7 @@
     private PendingIntent mPollIntent;
 
     private static final String PREFIX_DEV = "dev";
+    private static final String PREFIX_XT = "xt";
     private static final String PREFIX_UID = "uid";
     private static final String PREFIX_UID_TAG = "uid_tag";
 
@@ -187,6 +188,7 @@
         }
 
         public Config getDevConfig();
+        public Config getXtConfig();
         public Config getUidConfig();
         public Config getUidTagConfig();
     }
@@ -204,6 +206,7 @@
             new DropBoxNonMonotonicObserver();
 
     private NetworkStatsRecorder mDevRecorder;
+    private NetworkStatsRecorder mXtRecorder;
     private NetworkStatsRecorder mUidRecorder;
     private NetworkStatsRecorder mUidTagRecorder;
 
@@ -268,6 +271,7 @@
 
         // create data recorders along with historical rotators
         mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
+        mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false);
         mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
         mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
 
@@ -343,10 +347,12 @@
 
         // persist any pending stats
         mDevRecorder.forcePersistLocked(currentTime);
+        mXtRecorder.forcePersistLocked(currentTime);
         mUidRecorder.forcePersistLocked(currentTime);
         mUidTagRecorder.forcePersistLocked(currentTime);
 
         mDevRecorder = null;
+        mXtRecorder = null;
         mUidRecorder = null;
         mUidTagRecorder = null;
 
@@ -772,9 +778,11 @@
             // snapshot and record current counters; read UID stats first to
             // avoid overcounting dev stats.
             final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
-            final NetworkStats devSnapshot = getNetworkStatsSummary();
+            final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+            final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
 
             mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
+            mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
             mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
             mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
 
@@ -824,9 +832,11 @@
             // snapshot and record current counters; read UID stats first to
             // avoid overcounting dev stats.
             final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
-            final NetworkStats devSnapshot = getNetworkStatsSummary();
+            final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+            final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
 
             mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
+            mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
             mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
             mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
 
@@ -841,11 +851,13 @@
         // persist any pending data depending on requested flags
         if (persistForce) {
             mDevRecorder.forcePersistLocked(currentTime);
+            mXtRecorder.forcePersistLocked(currentTime);
             mUidRecorder.forcePersistLocked(currentTime);
             mUidTagRecorder.forcePersistLocked(currentTime);
         } else {
             if (persistNetwork) {
                 mDevRecorder.maybePersistLocked(currentTime);
+                mXtRecorder.maybePersistLocked(currentTime);
             }
             if (persistUid) {
                 mUidRecorder.maybePersistLocked(currentTime);
@@ -884,7 +896,7 @@
         // collect mobile sample
         template = buildTemplateMobileWildcard();
         devTotal = mDevRecorder.getTotalSinceBootLocked(template);
-        xtTotal = new NetworkStats.Entry();
+        xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
         EventLogTags.writeNetstatsMobileSample(
@@ -896,7 +908,7 @@
         // collect wifi sample
         template = buildTemplateWifiWildcard();
         devTotal = mDevRecorder.getTotalSinceBootLocked(template);
-        xtTotal = new NetworkStats.Entry();
+        xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
         EventLogTags.writeNetstatsWifiSample(
@@ -970,6 +982,11 @@
             mDevRecorder.dumpLocked(pw, fullHistory);
             pw.decreaseIndent();
 
+            pw.println("Xt stats:");
+            pw.increaseIndent();
+            mXtRecorder.dumpLocked(pw, fullHistory);
+            pw.decreaseIndent();
+
             if (includeUid) {
                 pw.println("UID stats:");
                 pw.increaseIndent();
@@ -986,10 +1003,6 @@
         }
     }
 
-    private NetworkStats getNetworkStatsSummary() throws RemoteException {
-        return mNetworkManager.getNetworkStatsSummary();
-    }
-
     /**
      * Return snapshot of current UID statistics, including any
      * {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
@@ -1126,6 +1139,11 @@
         }
 
         @Override
+        public Config getXtConfig() {
+            return getDevConfig();
+        }
+
+        @Override
         public Config getUidConfig() {
             return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
                     getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES),
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index ba3fd3c..9b371ac 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -106,8 +106,9 @@
 
     private static final long TEST_START = 1194220800000L;
     private static final String TEST_IFACE = "test0";
+    private static final String TEST_SSID = "AndroidAP";
 
-    private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
+    private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
 
     private BroadcastInterceptingContext mServiceContext;
     private File mPolicyDir;
@@ -860,7 +861,7 @@
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
-        return new NetworkState(info, prop, null);
+        return new NetworkState(info, prop, null, null, TEST_SSID);
     }
 
     private void expectCurrentTime() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 6d9bb29..10f8e61 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -30,7 +30,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.net.TrafficStats.UID_TETHERING;
@@ -94,7 +94,7 @@
     private static final String IMSI_1 = "310004";
     private static final String IMSI_2 = "310260";
 
-    private static NetworkTemplate sTemplateWifi = buildTemplateWifi();
+    private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
     private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
     private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
 
@@ -136,7 +136,6 @@
         mService = new NetworkStatsService(
                 mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir, mSettings);
         mService.bindConnectivityManager(mConnManager);
-        mSession = mService.openSession();
 
         mElapsedRealtime = 0L;
 
@@ -154,6 +153,7 @@
 
         replay();
         mService.systemReady();
+        mSession = mService.openSession();
         verifyAndReset();
 
         mNetworkObserver = networkObserver.getValue();
@@ -820,7 +820,8 @@
     }
 
     private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
-        expect(mNetManager.getNetworkStatsSummary()).andReturn(summary).atLeastOnce();
+        expect(mNetManager.getNetworkStatsSummaryDev()).andReturn(summary).atLeastOnce();
+        expect(mNetManager.getNetworkStatsSummaryXt()).andReturn(summary).atLeastOnce();
     }
 
     private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
@@ -851,6 +852,7 @@
 
         final Config config = new Config(bucketDuration, persistBytes, deleteAge, deleteAge);
         expect(mSettings.getDevConfig()).andReturn(config).anyTimes();
+        expect(mSettings.getXtConfig()).andReturn(config).anyTimes();
         expect(mSettings.getUidConfig()).andReturn(config).anyTimes();
         expect(mSettings.getUidTagConfig()).andReturn(config).anyTimes();
     }
@@ -912,7 +914,7 @@
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
-        return new NetworkState(info, prop, null, subscriberId);
+        return new NetworkState(info, prop, null, subscriberId, null);
     }
 
     private static NetworkState buildMobile4gState(String iface) {
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index 6a9778e..afa0eec 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -232,7 +232,8 @@
         final INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
 
         // test is currently no-op, just exercises stats apis
-        Log.d(TAG, nmService.getNetworkStatsSummary().toString());
+        Log.d(TAG, nmService.getNetworkStatsSummaryDev().toString());
+        Log.d(TAG, nmService.getNetworkStatsSummaryXt().toString());
         Log.d(TAG, nmService.getNetworkStatsDetail().toString());
     }
 
@@ -286,7 +287,7 @@
     }
 
     /**
-     * Expect {@link NetworkManagementService#getNetworkStatsSummary()} mock
+     * Expect {@link NetworkManagementService#getNetworkStatsSummaryDev()} mock
      * calls, responding with the given counter values.
      */
     public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
@@ -294,7 +295,7 @@
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
         stats.addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, rx, 0L, tx, 0L, 0);
 
-        expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
+        expect(mMockNMService.getNetworkStatsSummaryDev()).andReturn(stats).atLeastOnce();
     }
 
     /**