Merge "API review fixes" into jb-dev
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 08d4c6c..b7b8731 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -42,5 +42,7 @@
     void setUidForeground(int uid, boolean uidForeground);
     /** Force update of statistics. */
     void forceUpdate();
+    /** Advise persistance threshold; may be overridden internally. */
+    void advisePersistThreshold(long thresholdBytes);
 
 }
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/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/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java
index 2892a74..c2e475a 100644
--- a/services/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/java/com/android/server/net/NetworkStatsCollection.java
@@ -186,12 +186,12 @@
         if (history.size() == 0) return;
         noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
 
-        final NetworkStatsHistory existing = mStats.get(key);
-        if (existing != null) {
-            existing.recordEntireHistory(history);
-        } else {
-            mStats.put(key, history);
+        NetworkStatsHistory target = mStats.get(key);
+        if (target == null) {
+            target = new NetworkStatsHistory(history.getBucketDuration());
+            mStats.put(key, target);
         }
+        target.recordEntireHistory(history);
     }
 
     /**
diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java
index 57ad158..2ce7771 100644
--- a/services/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/java/com/android/server/net/NetworkStatsRecorder.java
@@ -17,6 +17,8 @@
 package com.android.server.net;
 
 import static android.net.NetworkStats.TAG_NONE;
+import static android.net.TrafficStats.KB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.net.NetworkStats;
@@ -25,6 +27,7 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Slog;
 
 import com.android.internal.util.FileRotator;
@@ -58,9 +61,9 @@
     private final String mCookie;
 
     private final long mBucketDuration;
-    private final long mPersistThresholdBytes;
     private final boolean mOnlyTags;
 
+    private long mPersistThresholdBytes = 2 * MB_IN_BYTES;
     private NetworkStats mLastSnapshot;
 
     private final NetworkStatsCollection mPending;
@@ -71,13 +74,12 @@
     private WeakReference<NetworkStatsCollection> mComplete;
 
     public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
-            String cookie, long bucketDuration, long persistThresholdBytes, boolean onlyTags) {
+            String cookie, long bucketDuration, boolean onlyTags) {
         mRotator = checkNotNull(rotator, "missing FileRotator");
         mObserver = checkNotNull(observer, "missing NonMonotonicObserver");
         mCookie = cookie;
 
         mBucketDuration = bucketDuration;
-        mPersistThresholdBytes = persistThresholdBytes;
         mOnlyTags = onlyTags;
 
         mPending = new NetworkStatsCollection(bucketDuration);
@@ -86,6 +88,12 @@
         mPendingRewriter = new CombiningRewriter(mPending);
     }
 
+    public void setPersistThreshold(long thresholdBytes) {
+        if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
+        mPersistThresholdBytes = MathUtils.constrain(
+                thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
+    }
+
     public void resetLocked() {
         mLastSnapshot = null;
         mPending.reset();
@@ -128,6 +136,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;
@@ -150,7 +161,7 @@
                 continue;
             }
 
-            // skip when no delta occured
+            // skip when no delta occurred
             if (entry.isEmpty()) continue;
 
             // only record tag data when requested
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 1c3e24f..1a56b80 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -36,6 +36,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
+import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
 import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
@@ -49,6 +50,10 @@
 import static android.provider.Settings.Secure.NETSTATS_UID_DELETE_AGE;
 import static android.provider.Settings.Secure.NETSTATS_UID_PERSIST_BYTES;
 import static android.provider.Settings.Secure.NETSTATS_UID_ROTATE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_UID_TAG_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_UID_TAG_DELETE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_UID_TAG_PERSIST_BYTES;
+import static android.provider.Settings.Secure.NETSTATS_UID_TAG_ROTATE_AGE;
 import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
 import static android.telephony.PhoneStateListener.LISTEN_NONE;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -94,10 +99,12 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.NtpTrustedTime;
 import android.util.Slog;
 import android.util.SparseIntArray;
@@ -159,6 +166,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";
 
@@ -168,27 +176,30 @@
     public interface NetworkStatsSettings {
         public long getPollInterval();
         public long getTimeCacheMaxAge();
-        public long getGlobalAlertBytes();
         public boolean getSampleEnabled();
 
         public static class Config {
             public final long bucketDuration;
-            public final long persistBytes;
             public final long rotateAgeMillis;
             public final long deleteAgeMillis;
 
-            public Config(long bucketDuration, long persistBytes, long rotateAgeMillis,
-                    long deleteAgeMillis) {
+            public Config(long bucketDuration, long rotateAgeMillis, long deleteAgeMillis) {
                 this.bucketDuration = bucketDuration;
-                this.persistBytes = persistBytes;
                 this.rotateAgeMillis = rotateAgeMillis;
                 this.deleteAgeMillis = deleteAgeMillis;
             }
         }
 
         public Config getDevConfig();
+        public Config getXtConfig();
         public Config getUidConfig();
         public Config getUidTagConfig();
+
+        public long getGlobalAlertBytes(long def);
+        public long getDevPersistBytes(long def);
+        public long getXtPersistBytes(long def);
+        public long getUidPersistBytes(long def);
+        public long getUidTagPersistBytes(long def);
     }
 
     private final Object mStatsLock = new Object();
@@ -204,6 +215,7 @@
             new DropBoxNonMonotonicObserver();
 
     private NetworkStatsRecorder mDevRecorder;
+    private NetworkStatsRecorder mXtRecorder;
     private NetworkStatsRecorder mUidRecorder;
     private NetworkStatsRecorder mUidTagRecorder;
 
@@ -220,6 +232,8 @@
     private final Handler mHandler;
 
     private boolean mSystemReady;
+    private long mPersistThreshold = 2 * MB_IN_BYTES;
+    private long mGlobalAlertBytes;
 
     public NetworkStatsService(
             Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
@@ -268,9 +282,12 @@
 
         // 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);
 
+        updatePersistThresholds();
+
         synchronized (mStatsLock) {
             // upgrade any legacy stats, migrating them to rotated files
             maybeUpgradeLegacyStatsLocked();
@@ -321,10 +338,9 @@
 
     private NetworkStatsRecorder buildRecorder(
             String prefix, NetworkStatsSettings.Config config, boolean includeTags) {
-        return new NetworkStatsRecorder(
-                new FileRotator(mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
-                mNonMonotonicObserver, prefix, config.bucketDuration, config.persistBytes,
-                includeTags);
+        return new NetworkStatsRecorder(new FileRotator(
+                mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
+                mNonMonotonicObserver, prefix, config.bucketDuration, includeTags);
     }
 
     private void shutdownLocked() {
@@ -343,10 +359,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;
 
@@ -408,8 +426,7 @@
      */
     private void registerGlobalAlert() {
         try {
-            final long alertBytes = mSettings.getGlobalAlertBytes();
-            mNetworkManager.setGlobalAlert(alertBytes);
+            mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
         } catch (IllegalStateException e) {
             Slog.w(TAG, "problem registering for global alert: " + e);
         } catch (RemoteException e) {
@@ -431,14 +448,18 @@
 
             private NetworkStatsCollection getUidComplete() {
                 if (mUidComplete == null) {
-                    mUidComplete = mUidRecorder.getOrLoadCompleteLocked();
+                    synchronized (mStatsLock) {
+                        mUidComplete = mUidRecorder.getOrLoadCompleteLocked();
+                    }
                 }
                 return mUidComplete;
             }
 
             private NetworkStatsCollection getUidTagComplete() {
                 if (mUidTagComplete == null) {
-                    mUidTagComplete = mUidTagRecorder.getOrLoadCompleteLocked();
+                    synchronized (mStatsLock) {
+                        mUidTagComplete = mUidTagRecorder.getOrLoadCompleteLocked();
+                    }
                 }
                 return mUidTagComplete;
             }
@@ -578,6 +599,47 @@
         }
     }
 
+    @Override
+    public void advisePersistThreshold(long thresholdBytes) {
+        mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        assertBandwidthControlEnabled();
+
+        // clamp threshold into safe range
+        mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
+        updatePersistThresholds();
+
+        if (LOGV) {
+            Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+                    + mPersistThreshold);
+        }
+
+        // persist if beyond new thresholds
+        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+                : System.currentTimeMillis();
+        synchronized (mStatsLock) {
+            mDevRecorder.maybePersistLocked(currentTime);
+            mXtRecorder.maybePersistLocked(currentTime);
+            mUidRecorder.maybePersistLocked(currentTime);
+            mUidTagRecorder.maybePersistLocked(currentTime);
+        }
+
+        // re-arm global alert
+        registerGlobalAlert();
+    }
+
+    /**
+     * Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to
+     * reflect current {@link #mPersistThreshold} value. Always defers to
+     * {@link Secure} values when defined.
+     */
+    private void updatePersistThresholds() {
+        mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold));
+        mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold));
+        mUidRecorder.setPersistThreshold(mSettings.getUidPersistBytes(mPersistThreshold));
+        mUidTagRecorder.setPersistThreshold(mSettings.getUidTagPersistBytes(mPersistThreshold));
+        mGlobalAlertBytes = mSettings.getGlobalAlertBytes(mPersistThreshold);
+    }
+
     /**
      * Receiver that watches for {@link IConnectivityManager} to claim network
      * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
@@ -772,9 +834,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 +888,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 +907,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 +952,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 +964,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 +1038,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 +1059,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.
@@ -1109,36 +1178,50 @@
             return getSecureLong(NETSTATS_TIME_CACHE_MAX_AGE, DAY_IN_MILLIS);
         }
         @Override
-        public long getGlobalAlertBytes() {
-            return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, 2 * MB_IN_BYTES);
+        public long getGlobalAlertBytes(long def) {
+            return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
         }
         @Override
         public boolean getSampleEnabled() {
             return getSecureBoolean(NETSTATS_SAMPLE_ENABLED, true);
         }
-
         @Override
         public Config getDevConfig() {
             return new Config(getSecureLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
-                    getSecureLong(NETSTATS_DEV_PERSIST_BYTES, 2 * MB_IN_BYTES),
                     getSecureLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
                     getSecureLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
         }
-
+        @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),
                     getSecureLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
                     getSecureLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
         }
-
         @Override
         public Config getUidTagConfig() {
-            return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
-                    getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES),
-                    getSecureLong(NETSTATS_UID_ROTATE_AGE, 5 * DAY_IN_MILLIS),
-                    getSecureLong(NETSTATS_UID_DELETE_AGE, 15 * DAY_IN_MILLIS));
+            return new Config(getSecureLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+                    getSecureLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
+                    getSecureLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
+        }
+        @Override
+        public long getDevPersistBytes(long def) {
+            return getSecureLong(NETSTATS_DEV_PERSIST_BYTES, def);
+        }
+        @Override
+        public long getXtPersistBytes(long def) {
+            return getDevPersistBytes(def);
+        }
+        @Override
+        public long getUidPersistBytes(long def) {
+            return getSecureLong(NETSTATS_UID_PERSIST_BYTES, def);
+        }
+        @Override
+        public long getUidTagPersistBytes(long def) {
+            return getSecureLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
         }
     }
 }