NetworkStats.java revision 4a97122ebf4d92a3f94402041729d77905e6c0c0
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.net; 18 19import android.os.Parcel; 20import android.os.Parcelable; 21import android.os.SystemClock; 22import android.util.SparseBooleanArray; 23 24import java.io.CharArrayWriter; 25import java.io.PrintWriter; 26import java.util.Arrays; 27import java.util.HashSet; 28 29/** 30 * Collection of active network statistics. Can contain summary details across 31 * all interfaces, or details with per-UID granularity. Internally stores data 32 * as a large table, closely matching {@code /proc/} data format. This structure 33 * optimizes for rapid in-memory comparison, but consider using 34 * {@link NetworkStatsHistory} when persisting. 35 * 36 * @hide 37 */ 38public class NetworkStats implements Parcelable { 39 /** {@link #iface} value when interface details unavailable. */ 40 public static final String IFACE_ALL = null; 41 /** {@link #uid} value when UID details unavailable. */ 42 public static final int UID_ALL = -1; 43 44 // NOTE: data should only be accounted for once in this structure; if data 45 // is broken out, the summarized version should not be included. 46 47 /** 48 * {@link SystemClock#elapsedRealtime()} timestamp when this data was 49 * generated. 50 */ 51 public final long elapsedRealtime; 52 public int size; 53 public String[] iface; 54 public int[] uid; 55 public long[] rx; 56 public long[] tx; 57 58 // TODO: add fg/bg stats once reported by kernel 59 60 public NetworkStats(long elapsedRealtime, int initialSize) { 61 this.elapsedRealtime = elapsedRealtime; 62 this.size = 0; 63 this.iface = new String[initialSize]; 64 this.uid = new int[initialSize]; 65 this.rx = new long[initialSize]; 66 this.tx = new long[initialSize]; 67 } 68 69 public NetworkStats(Parcel parcel) { 70 elapsedRealtime = parcel.readLong(); 71 size = parcel.readInt(); 72 iface = parcel.createStringArray(); 73 uid = parcel.createIntArray(); 74 rx = parcel.createLongArray(); 75 tx = parcel.createLongArray(); 76 } 77 78 public NetworkStats addEntry(String iface, int uid, long rx, long tx) { 79 if (size >= this.iface.length) { 80 final int newLength = Math.max(this.iface.length, 10) * 3 / 2; 81 this.iface = Arrays.copyOf(this.iface, newLength); 82 this.uid = Arrays.copyOf(this.uid, newLength); 83 this.rx = Arrays.copyOf(this.rx, newLength); 84 this.tx = Arrays.copyOf(this.tx, newLength); 85 } 86 87 this.iface[size] = iface; 88 this.uid[size] = uid; 89 this.rx[size] = rx; 90 this.tx[size] = tx; 91 size++; 92 93 return this; 94 } 95 96 @Deprecated 97 public int length() { 98 return size; 99 } 100 101 /** 102 * Find first stats index that matches the requested parameters. 103 */ 104 public int findIndex(String iface, int uid) { 105 for (int i = 0; i < size; i++) { 106 if (equal(iface, this.iface[i]) && uid == this.uid[i]) { 107 return i; 108 } 109 } 110 return -1; 111 } 112 113 /** 114 * Return list of unique interfaces known by this data structure. 115 */ 116 public String[] getUniqueIfaces() { 117 final HashSet<String> ifaces = new HashSet<String>(); 118 for (String iface : this.iface) { 119 if (iface != IFACE_ALL) { 120 ifaces.add(iface); 121 } 122 } 123 return ifaces.toArray(new String[ifaces.size()]); 124 } 125 126 /** 127 * Return list of unique UIDs known by this data structure. 128 */ 129 public int[] getUniqueUids() { 130 final SparseBooleanArray uids = new SparseBooleanArray(); 131 for (int uid : this.uid) { 132 uids.put(uid, true); 133 } 134 135 final int size = uids.size(); 136 final int[] result = new int[size]; 137 for (int i = 0; i < size; i++) { 138 result[i] = uids.keyAt(i); 139 } 140 return result; 141 } 142 143 /** 144 * Subtract the given {@link NetworkStats}, effectively leaving the delta 145 * between two snapshots in time. Assumes that statistics rows collect over 146 * time, and that none of them have disappeared. 147 * 148 * @throws IllegalArgumentException when given {@link NetworkStats} is 149 * non-monotonic. 150 */ 151 public NetworkStats subtract(NetworkStats value) { 152 return subtract(value, true, false); 153 } 154 155 /** 156 * Subtract the given {@link NetworkStats}, effectively leaving the delta 157 * between two snapshots in time. Assumes that statistics rows collect over 158 * time, and that none of them have disappeared. 159 * <p> 160 * Instead of throwing when counters are non-monotonic, this variant clamps 161 * results to never be negative. 162 */ 163 public NetworkStats subtractClamped(NetworkStats value) { 164 return subtract(value, false, true); 165 } 166 167 /** 168 * Subtract the given {@link NetworkStats}, effectively leaving the delta 169 * between two snapshots in time. Assumes that statistics rows collect over 170 * time, and that none of them have disappeared. 171 * 172 * @param enforceMonotonic Validate that incoming value is strictly 173 * monotonic compared to this object. 174 * @param clampNegative Instead of throwing like {@code enforceMonotonic}, 175 * clamp resulting counters at 0 to prevent negative values. 176 */ 177 private NetworkStats subtract( 178 NetworkStats value, boolean enforceMonotonic, boolean clampNegative) { 179 final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime; 180 if (enforceMonotonic && deltaRealtime < 0) { 181 throw new IllegalArgumentException("found non-monotonic realtime"); 182 } 183 184 // result will have our rows, and elapsed time between snapshots 185 final NetworkStats result = new NetworkStats(deltaRealtime, size); 186 for (int i = 0; i < size; i++) { 187 final String iface = this.iface[i]; 188 final int uid = this.uid[i]; 189 190 // find remote row that matches, and subtract 191 final int j = value.findIndex(iface, uid); 192 if (j == -1) { 193 // newly appearing row, return entire value 194 result.addEntry(iface, uid, this.rx[i], this.tx[i]); 195 } else { 196 // existing row, subtract remote value 197 long rx = this.rx[i] - value.rx[j]; 198 long tx = this.tx[i] - value.tx[j]; 199 if (enforceMonotonic && (rx < 0 || tx < 0)) { 200 throw new IllegalArgumentException("found non-monotonic values"); 201 } 202 if (clampNegative) { 203 rx = Math.max(0, rx); 204 tx = Math.max(0, tx); 205 } 206 result.addEntry(iface, uid, rx, tx); 207 } 208 } 209 210 return result; 211 } 212 213 private static boolean equal(Object a, Object b) { 214 return a == b || (a != null && a.equals(b)); 215 } 216 217 public void dump(String prefix, PrintWriter pw) { 218 pw.print(prefix); 219 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); 220 for (int i = 0; i < iface.length; i++) { 221 pw.print(prefix); 222 pw.print(" iface="); pw.print(iface[i]); 223 pw.print(" uid="); pw.print(uid[i]); 224 pw.print(" rx="); pw.print(rx[i]); 225 pw.print(" tx="); pw.println(tx[i]); 226 } 227 } 228 229 @Override 230 public String toString() { 231 final CharArrayWriter writer = new CharArrayWriter(); 232 dump("", new PrintWriter(writer)); 233 return writer.toString(); 234 } 235 236 /** {@inheritDoc} */ 237 public int describeContents() { 238 return 0; 239 } 240 241 /** {@inheritDoc} */ 242 public void writeToParcel(Parcel dest, int flags) { 243 dest.writeLong(elapsedRealtime); 244 dest.writeInt(size); 245 dest.writeStringArray(iface); 246 dest.writeIntArray(uid); 247 dest.writeLongArray(rx); 248 dest.writeLongArray(tx); 249 } 250 251 public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { 252 public NetworkStats createFromParcel(Parcel in) { 253 return new NetworkStats(in); 254 } 255 256 public NetworkStats[] newArray(int size) { 257 return new NetworkStats[size]; 258 } 259 }; 260} 261