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