NetworkStats.java revision 3f3913550c10792edb8aecf66cc83c3db5c8b311
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.HashSet; 27 28/** 29 * Collection of active network statistics. Can contain summary details across 30 * all interfaces, or details with per-UID granularity. Internally stores data 31 * as a large table, closely matching {@code /proc/} data format. This structure 32 * optimizes for rapid in-memory comparison, but consider using 33 * {@link NetworkStatsHistory} when persisting. 34 * 35 * @hide 36 */ 37public class NetworkStats implements Parcelable { 38 /** {@link #iface} value when interface details unavailable. */ 39 public static final String IFACE_ALL = null; 40 /** {@link #uid} value when UID details unavailable. */ 41 public static final int UID_ALL = -1; 42 43 // NOTE: data should only be accounted for once in this structure; if data 44 // is broken out, the summarized version should not be included. 45 46 /** 47 * {@link SystemClock#elapsedRealtime()} timestamp when this data was 48 * generated. 49 */ 50 public final long elapsedRealtime; 51 public final String[] iface; 52 public final int[] uid; 53 public final long[] rx; 54 public final long[] tx; 55 56 // TODO: add fg/bg stats once reported by kernel 57 58 private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) { 59 this.elapsedRealtime = elapsedRealtime; 60 this.iface = iface; 61 this.uid = uid; 62 this.rx = rx; 63 this.tx = tx; 64 } 65 66 public NetworkStats(Parcel parcel) { 67 elapsedRealtime = parcel.readLong(); 68 iface = parcel.createStringArray(); 69 uid = parcel.createIntArray(); 70 rx = parcel.createLongArray(); 71 tx = parcel.createLongArray(); 72 } 73 74 public static class Builder { 75 private long mElapsedRealtime; 76 private final String[] mIface; 77 private final int[] mUid; 78 private final long[] mRx; 79 private final long[] mTx; 80 81 private int mIndex = 0; 82 83 public Builder(long elapsedRealtime, int size) { 84 mElapsedRealtime = elapsedRealtime; 85 mIface = new String[size]; 86 mUid = new int[size]; 87 mRx = new long[size]; 88 mTx = new long[size]; 89 } 90 91 public Builder addEntry(String iface, int uid, long rx, long tx) { 92 mIface[mIndex] = iface; 93 mUid[mIndex] = uid; 94 mRx[mIndex] = rx; 95 mTx[mIndex] = tx; 96 mIndex++; 97 return this; 98 } 99 100 public NetworkStats build() { 101 if (mIndex != mIface.length) { 102 throw new IllegalArgumentException("unexpected number of entries"); 103 } 104 return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx); 105 } 106 } 107 108 public int length() { 109 // length is identical for all fields 110 return iface.length; 111 } 112 113 /** 114 * Find first stats index that matches the requested parameters. 115 */ 116 public int findIndex(String iface, int uid) { 117 final int length = length(); 118 for (int i = 0; i < length; i++) { 119 if (equal(iface, this.iface[i]) && uid == this.uid[i]) { 120 return i; 121 } 122 } 123 return -1; 124 } 125 126 /** 127 * Return list of unique interfaces known by this data structure. 128 */ 129 public String[] getUniqueIfaces() { 130 final HashSet<String> ifaces = new HashSet<String>(); 131 for (String iface : this.iface) { 132 if (iface != IFACE_ALL) { 133 ifaces.add(iface); 134 } 135 } 136 return ifaces.toArray(new String[ifaces.size()]); 137 } 138 139 /** 140 * Return list of unique UIDs known by this data structure. 141 */ 142 public int[] getUniqueUids() { 143 final SparseBooleanArray uids = new SparseBooleanArray(); 144 for (int uid : this.uid) { 145 uids.put(uid, true); 146 } 147 148 final int size = uids.size(); 149 final int[] result = new int[size]; 150 for (int i = 0; i < size; i++) { 151 result[i] = uids.keyAt(i); 152 } 153 return result; 154 } 155 156 /** 157 * Subtract the given {@link NetworkStats}, effectively leaving the delta 158 * between two snapshots in time. Assumes that statistics rows collect over 159 * time, and that none of them have disappeared. 160 * 161 * @throws IllegalArgumentException when given {@link NetworkStats} is 162 * non-monotonic. 163 */ 164 public NetworkStats subtract(NetworkStats value) { 165 return subtract(value, true, false); 166 } 167 168 /** 169 * Subtract the given {@link NetworkStats}, effectively leaving the delta 170 * between two snapshots in time. Assumes that statistics rows collect over 171 * time, and that none of them have disappeared. 172 * <p> 173 * Instead of throwing when counters are non-monotonic, this variant clamps 174 * results to never be negative. 175 */ 176 public NetworkStats subtractClamped(NetworkStats value) { 177 return subtract(value, false, true); 178 } 179 180 /** 181 * Subtract the given {@link NetworkStats}, effectively leaving the delta 182 * between two snapshots in time. Assumes that statistics rows collect over 183 * time, and that none of them have disappeared. 184 * 185 * @param enforceMonotonic Validate that incoming value is strictly 186 * monotonic compared to this object. 187 * @param clampNegative Instead of throwing like {@code enforceMonotonic}, 188 * clamp resulting counters at 0 to prevent negative values. 189 */ 190 private NetworkStats subtract( 191 NetworkStats value, boolean enforceMonotonic, boolean clampNegative) { 192 final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime; 193 if (enforceMonotonic && deltaRealtime < 0) { 194 throw new IllegalArgumentException("found non-monotonic realtime"); 195 } 196 197 // result will have our rows, and elapsed time between snapshots 198 final int length = length(); 199 final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length); 200 for (int i = 0; i < length; i++) { 201 final String iface = this.iface[i]; 202 final int uid = this.uid[i]; 203 204 // find remote row that matches, and subtract 205 final int j = value.findIndex(iface, uid); 206 if (j == -1) { 207 // newly appearing row, return entire value 208 result.addEntry(iface, uid, this.rx[i], this.tx[i]); 209 } else { 210 // existing row, subtract remote value 211 long rx = this.rx[i] - value.rx[j]; 212 long tx = this.tx[i] - value.tx[j]; 213 if (enforceMonotonic && (rx < 0 || tx < 0)) { 214 throw new IllegalArgumentException("found non-monotonic values"); 215 } 216 if (clampNegative) { 217 rx = Math.max(0, rx); 218 tx = Math.max(0, tx); 219 } 220 result.addEntry(iface, uid, rx, tx); 221 } 222 } 223 224 return result.build(); 225 } 226 227 private static boolean equal(Object a, Object b) { 228 return a == b || (a != null && a.equals(b)); 229 } 230 231 public void dump(String prefix, PrintWriter pw) { 232 pw.print(prefix); 233 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); 234 for (int i = 0; i < iface.length; i++) { 235 pw.print(prefix); 236 pw.print(" iface="); pw.print(iface[i]); 237 pw.print(" uid="); pw.print(uid[i]); 238 pw.print(" rx="); pw.print(rx[i]); 239 pw.print(" tx="); pw.println(tx[i]); 240 } 241 } 242 243 @Override 244 public String toString() { 245 final CharArrayWriter writer = new CharArrayWriter(); 246 dump("", new PrintWriter(writer)); 247 return writer.toString(); 248 } 249 250 /** {@inheritDoc} */ 251 public int describeContents() { 252 return 0; 253 } 254 255 /** {@inheritDoc} */ 256 public void writeToParcel(Parcel dest, int flags) { 257 dest.writeLong(elapsedRealtime); 258 dest.writeStringArray(iface); 259 dest.writeIntArray(uid); 260 dest.writeLongArray(rx); 261 dest.writeLongArray(tx); 262 } 263 264 public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { 265 public NetworkStats createFromParcel(Parcel in) { 266 return new NetworkStats(in); 267 } 268 269 public NetworkStats[] newArray(int size) { 270 return new NetworkStats[size]; 271 } 272 }; 273} 274