Move non-monotonic reporting to interface.

Report non-monotonic NetworkStats through an observer interface
instead of throwing, since those events are still recoverable.

Change-Id: Ic0749f4634b0ac05dbe90e95ca490957ec8b2f23
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index f6e627c..e8f60b4 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,8 +16,6 @@
 
 package android.net;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -40,8 +38,6 @@
  * @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. */
@@ -463,62 +459,64 @@
      * between two snapshots in time. Assumes that statistics rows collect over
      * time, and that none of them have disappeared.
      */
-    public NetworkStats subtract(NetworkStats value) throws NonMonotonicException {
-        return subtract(value, false);
+    public NetworkStats subtract(NetworkStats right) {
+        return subtract(this, right, null);
     }
 
     /**
-     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * Subtract the two given {@link NetworkStats} objects, returning 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}.
+     * <p>
+     * If counters have rolled backwards, they are clamped to {@code 0} and
+     * reported to the given {@link NonMonotonicObserver}.
      */
-    public NetworkStats subtract(NetworkStats value, boolean clampNonMonotonic)
-            throws NonMonotonicException {
-        final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
+    public static NetworkStats subtract(
+            NetworkStats left, NetworkStats right, NonMonotonicObserver observer) {
+        long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
         if (deltaRealtime < 0) {
-            throw new NonMonotonicException(this, value);
+            if (observer != null) {
+                observer.foundNonMonotonic(left, -1, right, -1);
+            }
+            deltaRealtime = 0;
         }
 
         // result will have our rows, and elapsed time between snapshots
         final Entry entry = new Entry();
-        final NetworkStats result = new NetworkStats(deltaRealtime, size);
-        for (int i = 0; i < size; i++) {
-            entry.iface = iface[i];
-            entry.uid = uid[i];
-            entry.set = set[i];
-            entry.tag = tag[i];
+        final NetworkStats result = new NetworkStats(deltaRealtime, left.size);
+        for (int i = 0; i < left.size; i++) {
+            entry.iface = left.iface[i];
+            entry.uid = left.uid[i];
+            entry.set = left.set[i];
+            entry.tag = left.tag[i];
 
             // find remote row that matches, and subtract
-            final int j = value.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, i);
+            final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, i);
             if (j == -1) {
                 // newly appearing row, return entire value
-                entry.rxBytes = rxBytes[i];
-                entry.rxPackets = rxPackets[i];
-                entry.txBytes = txBytes[i];
-                entry.txPackets = txPackets[i];
-                entry.operations = operations[i];
+                entry.rxBytes = left.rxBytes[i];
+                entry.rxPackets = left.rxPackets[i];
+                entry.txBytes = left.txBytes[i];
+                entry.txPackets = left.txPackets[i];
+                entry.operations = left.operations[i];
             } else {
                 // existing row, subtract remote value
-                entry.rxBytes = rxBytes[i] - value.rxBytes[j];
-                entry.rxPackets = rxPackets[i] - value.rxPackets[j];
-                entry.txBytes = txBytes[i] - value.txBytes[j];
-                entry.txPackets = txPackets[i] - value.txPackets[j];
-                entry.operations = operations[i] - value.operations[j];
+                entry.rxBytes = left.rxBytes[i] - right.rxBytes[j];
+                entry.rxPackets = left.rxPackets[i] - right.rxPackets[j];
+                entry.txBytes = left.txBytes[i] - right.txBytes[j];
+                entry.txPackets = left.txPackets[i] - right.txPackets[j];
+                entry.operations = left.operations[i] - right.operations[j];
 
                 if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
                         || entry.txPackets < 0 || entry.operations < 0) {
-                    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);
+                    if (observer != null) {
+                        observer.foundNonMonotonic(left, i, right, j);
                     }
+                    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);
                 }
             }
 
@@ -665,22 +663,8 @@
         }
     };
 
-    public static class NonMonotonicException extends Exception {
-        public final NetworkStats left;
-        public final NetworkStats right;
-        public final int leftIndex;
-        public final int rightIndex;
-
-        public NonMonotonicException(NetworkStats left, NetworkStats right) {
-            this(left, -1, right, -1);
-        }
-
-        public NonMonotonicException(
-                NetworkStats left, int leftIndex, NetworkStats right, int rightIndex) {
-            this.left = checkNotNull(left, "missing left");
-            this.right = checkNotNull(right, "missing right");
-            this.leftIndex = leftIndex;
-            this.rightIndex = rightIndex;
-        }
+    public interface NonMonotonicObserver {
+        public void foundNonMonotonic(
+                NetworkStats left, int leftIndex, NetworkStats right, int rightIndex);
     }
 }