Clamp non-monotonic stats instead of dropping.

When encountering non-monotonic stats rows, recover remaining data by
clamping to 0.  In particular, this avoids edge-case where persisting
threshold checks would never trigger.  Also recover when tethering
snapshots are missing.

Bug: 5600785, 5433871, 5600678
Change-Id: I1871954ce3955cc4ac8846f9841bae0066176ffe
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 3605652..f6e627c 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -464,6 +464,19 @@
      * time, and that none of them have disappeared.
      */
     public NetworkStats subtract(NetworkStats value) throws NonMonotonicException {
+        return subtract(value, 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.
+     *
+     * @param clampNonMonotonic When non-monotonic stats are found, just clamp
+     *            to 0 instead of throwing {@link NonMonotonicException}.
+     */
+    public NetworkStats subtract(NetworkStats value, boolean clampNonMonotonic)
+            throws NonMonotonicException {
         final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
         if (deltaRealtime < 0) {
             throw new NonMonotonicException(this, value);
@@ -497,7 +510,15 @@
 
                 if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
                         || entry.txPackets < 0 || entry.operations < 0) {
-                    throw new NonMonotonicException(this, i, value, j);
+                    if (clampNonMonotonic) {
+                        entry.rxBytes = Math.max(entry.rxBytes, 0);
+                        entry.rxPackets = Math.max(entry.rxPackets, 0);
+                        entry.txBytes = Math.max(entry.txBytes, 0);
+                        entry.txPackets = Math.max(entry.txPackets, 0);
+                        entry.operations = Math.max(entry.operations, 0);
+                    } else {
+                        throw new NonMonotonicException(this, i, value, j);
+                    }
                 }
             }