NetworkStats.java revision 61ee0bbb5b87fb5c4c3dc219868d52743def3d2b
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 * @param enforceMonotonic Validate that incoming value is strictly 162 * monotonic compared to this object. 163 */ 164 public NetworkStats subtract(NetworkStats value, boolean enforceMonotonic) { 165 final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime; 166 if (enforceMonotonic && deltaRealtime < 0) { 167 throw new IllegalArgumentException("found non-monotonic realtime"); 168 } 169 170 // result will have our rows, and elapsed time between snapshots 171 final int length = length(); 172 final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length); 173 for (int i = 0; i < length; i++) { 174 final String iface = this.iface[i]; 175 final int uid = this.uid[i]; 176 177 // find remote row that matches, and subtract 178 final int j = value.findIndex(iface, uid); 179 if (j == -1) { 180 // newly appearing row, return entire value 181 result.addEntry(iface, uid, this.rx[i], this.tx[i]); 182 } else { 183 // existing row, subtract remote value 184 final long rx = this.rx[i] - value.rx[j]; 185 final long tx = this.tx[i] - value.tx[j]; 186 if (enforceMonotonic && (rx < 0 || tx < 0)) { 187 throw new IllegalArgumentException("found non-monotonic values"); 188 } 189 result.addEntry(iface, uid, rx, tx); 190 } 191 } 192 193 return result.build(); 194 } 195 196 private static boolean equal(Object a, Object b) { 197 return a == b || (a != null && a.equals(b)); 198 } 199 200 public void dump(String prefix, PrintWriter pw) { 201 pw.print(prefix); 202 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); 203 for (int i = 0; i < iface.length; i++) { 204 pw.print(prefix); 205 pw.print(" iface="); pw.print(iface[i]); 206 pw.print(" uid="); pw.print(uid[i]); 207 pw.print(" rx="); pw.print(rx[i]); 208 pw.print(" tx="); pw.println(tx[i]); 209 } 210 } 211 212 @Override 213 public String toString() { 214 final CharArrayWriter writer = new CharArrayWriter(); 215 dump("", new PrintWriter(writer)); 216 return writer.toString(); 217 } 218 219 /** {@inheritDoc} */ 220 public int describeContents() { 221 return 0; 222 } 223 224 /** {@inheritDoc} */ 225 public void writeToParcel(Parcel dest, int flags) { 226 dest.writeLong(elapsedRealtime); 227 dest.writeStringArray(iface); 228 dest.writeIntArray(uid); 229 dest.writeLongArray(rx); 230 dest.writeLongArray(tx); 231 } 232 233 public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { 234 public NetworkStats createFromParcel(Parcel in) { 235 return new NetworkStats(in); 236 } 237 238 public NetworkStats[] newArray(int size) { 239 return new NetworkStats[size]; 240 } 241 }; 242} 243