Persist network stats using AtomicFile.

Implements read/write of network stats using AtomicFile, along with
magic number and versioning.  Stores in "/data/system/netstats.bin"
for now.  Tests to verify that stats are persisted across a simulated
reboot, and to verify that TEMPLATE_WIFI is working.

Fixed bug where kernel counters rolling backwards would cause negative
stats to be recorded; now we clamp deltas at 0.

Change-Id: I53bce26fc8fd3f4ab1e34ce135d302edfa34db34
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index ee415fa..6354e9a 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -158,10 +158,37 @@
      * between two snapshots in time. Assumes that statistics rows collect over
      * time, and that none of them have disappeared.
      *
+     * @throws IllegalArgumentException when given {@link NetworkStats} is
+     *             non-monotonic.
+     */
+    public NetworkStats subtract(NetworkStats value) {
+        return subtract(value, true, false);
+    }
+
+    /**
+     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * between two snapshots in time. Assumes that statistics rows collect over
+     * time, and that none of them have disappeared.
+     * <p>
+     * Instead of throwing when counters are non-monotonic, this variant clamps
+     * results to never be negative.
+     */
+    public NetworkStats subtractClamped(NetworkStats value) {
+        return subtract(value, false, true);
+    }
+
+    /**
+     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * between two snapshots in time. Assumes that statistics rows collect over
+     * time, and that none of them have disappeared.
+     *
      * @param enforceMonotonic Validate that incoming value is strictly
      *            monotonic compared to this object.
+     * @param clampNegative Instead of throwing like {@code enforceMonotonic},
+     *            clamp resulting counters at 0 to prevent negative values.
      */
-    public NetworkStats subtract(NetworkStats value, boolean enforceMonotonic) {
+    private NetworkStats subtract(
+            NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
         final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
         if (enforceMonotonic && deltaRealtime < 0) {
             throw new IllegalArgumentException("found non-monotonic realtime");
@@ -181,11 +208,15 @@
                 result.addEntry(iface, uid, this.rx[i], this.tx[i]);
             } else {
                 // existing row, subtract remote value
-                final long rx = this.rx[i] - value.rx[j];
-                final long tx = this.tx[i] - value.tx[j];
+                long rx = this.rx[i] - value.rx[j];
+                long tx = this.tx[i] - value.tx[j];
                 if (enforceMonotonic && (rx < 0 || tx < 0)) {
                     throw new IllegalArgumentException("found non-monotonic values");
                 }
+                if (clampNegative) {
+                    rx = Math.max(0, rx);
+                    tx = Math.max(0, tx);
+                }
                 result.addEntry(iface, uid, rx, tx);
             }
         }