Improve how battery stats collects network stats.

This optimizes the path for battery stats to collect
per-uid network usage.  It now collects wifi and mobile
usage separately, with a path that allows it to recycle
all data structures and filter out stats it isn't
interested in before they come back to java.

This is setting us up for the actual goal, to collect
mobile stats independently each time the mobile radio
goes down, allowing us to distribute mobile radio usage
across uids based on the number of packets they transferred
during a session.

Change-Id: I21a0f517cf087ea5aa8b8dd535e20b46e361a52b
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a7aae2a..25514f4 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -44,6 +44,8 @@
     public static final String IFACE_ALL = null;
     /** {@link #uid} value when UID details unavailable. */
     public static final int UID_ALL = -1;
+    /** {@link #tag} value matching any tag. */
+    public static final int TAG_ALL = -1;
     /** {@link #set} value when all sets combined. */
     public static final int SET_ALL = -1;
     /** {@link #set} value where background data is accounted. */
@@ -59,8 +61,9 @@
      * {@link SystemClock#elapsedRealtime()} timestamp when this data was
      * generated.
      */
-    private final long elapsedRealtime;
+    private long elapsedRealtime;
     private int size;
+    private int capacity;
     private String[] iface;
     private int[] uid;
     private int[] set;
@@ -152,20 +155,27 @@
     public NetworkStats(long elapsedRealtime, int initialSize) {
         this.elapsedRealtime = elapsedRealtime;
         this.size = 0;
-        this.iface = new String[initialSize];
-        this.uid = new int[initialSize];
-        this.set = new int[initialSize];
-        this.tag = new int[initialSize];
-        this.rxBytes = new long[initialSize];
-        this.rxPackets = new long[initialSize];
-        this.txBytes = new long[initialSize];
-        this.txPackets = new long[initialSize];
-        this.operations = new long[initialSize];
+        if (initialSize >= 0) {
+            this.capacity = initialSize;
+            this.iface = new String[initialSize];
+            this.uid = new int[initialSize];
+            this.set = new int[initialSize];
+            this.tag = new int[initialSize];
+            this.rxBytes = new long[initialSize];
+            this.rxPackets = new long[initialSize];
+            this.txBytes = new long[initialSize];
+            this.txPackets = new long[initialSize];
+            this.operations = new long[initialSize];
+        } else {
+            // Special case for use by NetworkStatsFactory to start out *really* empty.
+            this.capacity = 0;
+        }
     }
 
     public NetworkStats(Parcel parcel) {
         elapsedRealtime = parcel.readLong();
         size = parcel.readInt();
+        capacity = parcel.readInt();
         iface = parcel.createStringArray();
         uid = parcel.createIntArray();
         set = parcel.createIntArray();
@@ -181,6 +191,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(elapsedRealtime);
         dest.writeInt(size);
+        dest.writeInt(capacity);
         dest.writeStringArray(iface);
         dest.writeIntArray(uid);
         dest.writeIntArray(set);
@@ -222,8 +233,8 @@
      * object can be recycled across multiple calls.
      */
     public NetworkStats addValues(Entry entry) {
-        if (size >= this.iface.length) {
-            final int newLength = Math.max(iface.length, 10) * 3 / 2;
+        if (size >= capacity) {
+            final int newLength = Math.max(size, 10) * 3 / 2;
             iface = Arrays.copyOf(iface, newLength);
             uid = Arrays.copyOf(uid, newLength);
             set = Arrays.copyOf(set, newLength);
@@ -233,6 +244,7 @@
             txBytes = Arrays.copyOf(txBytes, newLength);
             txPackets = Arrays.copyOf(txPackets, newLength);
             operations = Arrays.copyOf(operations, newLength);
+            capacity = newLength;
         }
 
         iface[size] = entry.iface;
@@ -270,6 +282,10 @@
         return elapsedRealtime;
     }
 
+    public void setElapsedRealtime(long time) {
+        elapsedRealtime = time;
+    }
+
     /**
      * Return age of this {@link NetworkStats} object with respect to
      * {@link SystemClock#elapsedRealtime()}.
@@ -284,7 +300,7 @@
 
     @VisibleForTesting
     public int internalSize() {
-        return iface.length;
+        return capacity;
     }
 
     @Deprecated
@@ -507,8 +523,25 @@
      * If counters have rolled backwards, they are clamped to {@code 0} and
      * reported to the given {@link NonMonotonicObserver}.
      */
-    public static <C> NetworkStats subtract(
-            NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie) {
+    public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
+            NonMonotonicObserver<C> observer, C cookie) {
+        return subtract(left, right, observer, cookie, null);
+    }
+
+    /**
+     * 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.
+     * <p>
+     * If counters have rolled backwards, they are clamped to {@code 0} and
+     * reported to the given {@link NonMonotonicObserver}.
+     * <p>
+     * If <var>recycle</var> is supplied, this NetworkStats object will be
+     * reused (and returned) as the result if it is large enough to contain
+     * the data.
+     */
+    public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
+            NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) {
         long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
         if (deltaRealtime < 0) {
             if (observer != null) {
@@ -519,7 +552,14 @@
 
         // result will have our rows, and elapsed time between snapshots
         final Entry entry = new Entry();
-        final NetworkStats result = new NetworkStats(deltaRealtime, left.size);
+        final NetworkStats result;
+        if (recycle != null && recycle.capacity >= left.size) {
+            result = recycle;
+            result.size = 0;
+            result.elapsedRealtime = deltaRealtime;
+        } else {
+            result = new NetworkStats(deltaRealtime, left.size);
+        }
         for (int i = 0; i < left.size; i++) {
             entry.iface = left.iface[i];
             entry.uid = left.uid[i];