blob: 9d40c42a39d7eaa27da4d6cd41932cf7e7020cc2 [file] [log] [blame]
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001/*
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;
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -070022import android.util.SparseBooleanArray;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070023
24import java.io.CharArrayWriter;
25import java.io.PrintWriter;
Jeff Sharkey4a971222011-06-11 22:16:55 -070026import java.util.Arrays;
Jeff Sharkey75279902011-05-24 18:39:45 -070027import java.util.HashSet;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070028
29/**
Jeff Sharkey75279902011-05-24 18:39:45 -070030 * 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.
Jeff Sharkey9a13f362011-04-26 16:25:36 -070035 *
36 * @hide
37 */
38public class NetworkStats implements Parcelable {
Jeff Sharkey75279902011-05-24 18:39:45 -070039 /** {@link #iface} value when interface details unavailable. */
Jeff Sharkey9a13f362011-04-26 16:25:36 -070040 public static final String IFACE_ALL = null;
Jeff Sharkey75279902011-05-24 18:39:45 -070041 /** {@link #uid} value when UID details unavailable. */
42 public static final int UID_ALL = -1;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070043 /** {@link #tag} value for without tag. */
44 public static final int TAG_NONE = 0;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070045
Jeff Sharkey9a13f362011-04-26 16:25:36 -070046 /**
47 * {@link SystemClock#elapsedRealtime()} timestamp when this data was
48 * generated.
49 */
50 public final long elapsedRealtime;
Jeff Sharkey4a971222011-06-11 22:16:55 -070051 public int size;
52 public String[] iface;
53 public int[] uid;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070054 public int[] tag;
Jeff Sharkey4a971222011-06-11 22:16:55 -070055 public long[] rx;
56 public long[] tx;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070057
Jeff Sharkey4a971222011-06-11 22:16:55 -070058 public NetworkStats(long elapsedRealtime, int initialSize) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -070059 this.elapsedRealtime = elapsedRealtime;
Jeff Sharkey4a971222011-06-11 22:16:55 -070060 this.size = 0;
61 this.iface = new String[initialSize];
62 this.uid = new int[initialSize];
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070063 this.tag = new int[initialSize];
Jeff Sharkey4a971222011-06-11 22:16:55 -070064 this.rx = new long[initialSize];
65 this.tx = new long[initialSize];
Jeff Sharkey9a13f362011-04-26 16:25:36 -070066 }
67
68 public NetworkStats(Parcel parcel) {
69 elapsedRealtime = parcel.readLong();
Jeff Sharkey4a971222011-06-11 22:16:55 -070070 size = parcel.readInt();
Jeff Sharkey9a13f362011-04-26 16:25:36 -070071 iface = parcel.createStringArray();
72 uid = parcel.createIntArray();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070073 tag = parcel.createIntArray();
Jeff Sharkey9a13f362011-04-26 16:25:36 -070074 rx = parcel.createLongArray();
75 tx = parcel.createLongArray();
76 }
77
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070078 /**
79 * Add new stats entry with given values.
80 */
81 public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) {
Jeff Sharkey4a971222011-06-11 22:16:55 -070082 if (size >= this.iface.length) {
83 final int newLength = Math.max(this.iface.length, 10) * 3 / 2;
84 this.iface = Arrays.copyOf(this.iface, newLength);
85 this.uid = Arrays.copyOf(this.uid, newLength);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070086 this.tag = Arrays.copyOf(this.tag, newLength);
Jeff Sharkey4a971222011-06-11 22:16:55 -070087 this.rx = Arrays.copyOf(this.rx, newLength);
88 this.tx = Arrays.copyOf(this.tx, newLength);
Jeff Sharkey9a13f362011-04-26 16:25:36 -070089 }
90
Jeff Sharkey4a971222011-06-11 22:16:55 -070091 this.iface[size] = iface;
92 this.uid[size] = uid;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070093 this.tag[size] = tag;
Jeff Sharkey4a971222011-06-11 22:16:55 -070094 this.rx[size] = rx;
95 this.tx[size] = tx;
96 size++;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070097
Jeff Sharkey4a971222011-06-11 22:16:55 -070098 return this;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070099 }
100
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700101 /**
102 * Combine given values with an existing row, or create a new row if
103 * {@link #findIndex(String, int, int)} is unable to find match. Can also be
104 * used to subtract values from existing rows.
105 */
106 public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) {
107 final int i = findIndex(iface, uid, tag);
108 if (i == -1) {
109 // only create new entry when positive contribution
110 addEntry(iface, uid, tag, rx, tx);
111 } else {
112 this.rx[i] += rx;
113 this.tx[i] += tx;
114 }
115 return this;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700116 }
117
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700118 /**
119 * Find first stats index that matches the requested parameters.
120 */
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700121 public int findIndex(String iface, int uid, int tag) {
Jeff Sharkey4a971222011-06-11 22:16:55 -0700122 for (int i = 0; i < size; i++) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700123 if (equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700124 return i;
125 }
126 }
127 return -1;
128 }
129
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700130 /**
Jeff Sharkey75279902011-05-24 18:39:45 -0700131 * Return list of unique interfaces known by this data structure.
132 */
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700133 public String[] getUniqueIfaces() {
Jeff Sharkey75279902011-05-24 18:39:45 -0700134 final HashSet<String> ifaces = new HashSet<String>();
135 for (String iface : this.iface) {
136 if (iface != IFACE_ALL) {
137 ifaces.add(iface);
138 }
139 }
140 return ifaces.toArray(new String[ifaces.size()]);
141 }
142
143 /**
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700144 * Return list of unique UIDs known by this data structure.
145 */
146 public int[] getUniqueUids() {
147 final SparseBooleanArray uids = new SparseBooleanArray();
148 for (int uid : this.uid) {
149 uids.put(uid, true);
150 }
151
152 final int size = uids.size();
153 final int[] result = new int[size];
154 for (int i = 0; i < size; i++) {
155 result[i] = uids.keyAt(i);
156 }
157 return result;
158 }
159
160 /**
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700161 * Subtract the given {@link NetworkStats}, effectively leaving the delta
162 * between two snapshots in time. Assumes that statistics rows collect over
163 * time, and that none of them have disappeared.
Jeff Sharkey75279902011-05-24 18:39:45 -0700164 *
Jeff Sharkey3f391352011-06-05 17:42:53 -0700165 * @throws IllegalArgumentException when given {@link NetworkStats} is
166 * non-monotonic.
167 */
168 public NetworkStats subtract(NetworkStats value) {
169 return subtract(value, true, false);
170 }
171
172 /**
173 * Subtract the given {@link NetworkStats}, effectively leaving the delta
174 * between two snapshots in time. Assumes that statistics rows collect over
175 * time, and that none of them have disappeared.
176 * <p>
177 * Instead of throwing when counters are non-monotonic, this variant clamps
178 * results to never be negative.
179 */
180 public NetworkStats subtractClamped(NetworkStats value) {
181 return subtract(value, false, true);
182 }
183
184 /**
185 * Subtract the given {@link NetworkStats}, effectively leaving the delta
186 * between two snapshots in time. Assumes that statistics rows collect over
187 * time, and that none of them have disappeared.
188 *
Jeff Sharkey75279902011-05-24 18:39:45 -0700189 * @param enforceMonotonic Validate that incoming value is strictly
190 * monotonic compared to this object.
Jeff Sharkey3f391352011-06-05 17:42:53 -0700191 * @param clampNegative Instead of throwing like {@code enforceMonotonic},
192 * clamp resulting counters at 0 to prevent negative values.
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700193 */
Jeff Sharkey3f391352011-06-05 17:42:53 -0700194 private NetworkStats subtract(
195 NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700196 final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
197 if (enforceMonotonic && deltaRealtime < 0) {
198 throw new IllegalArgumentException("found non-monotonic realtime");
199 }
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700200
Jeff Sharkey75279902011-05-24 18:39:45 -0700201 // result will have our rows, and elapsed time between snapshots
Jeff Sharkey4a971222011-06-11 22:16:55 -0700202 final NetworkStats result = new NetworkStats(deltaRealtime, size);
203 for (int i = 0; i < size; i++) {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700204 final String iface = this.iface[i];
205 final int uid = this.uid[i];
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700206 final int tag = this.tag[i];
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700207
208 // find remote row that matches, and subtract
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700209 final int j = value.findIndex(iface, uid, tag);
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700210 if (j == -1) {
211 // newly appearing row, return entire value
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700212 result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]);
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700213 } else {
214 // existing row, subtract remote value
Jeff Sharkey3f391352011-06-05 17:42:53 -0700215 long rx = this.rx[i] - value.rx[j];
216 long tx = this.tx[i] - value.tx[j];
Jeff Sharkey75279902011-05-24 18:39:45 -0700217 if (enforceMonotonic && (rx < 0 || tx < 0)) {
218 throw new IllegalArgumentException("found non-monotonic values");
219 }
Jeff Sharkey3f391352011-06-05 17:42:53 -0700220 if (clampNegative) {
221 rx = Math.max(0, rx);
222 tx = Math.max(0, tx);
223 }
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700224 result.addEntry(iface, uid, tag, rx, tx);
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700225 }
226 }
227
Jeff Sharkey4a971222011-06-11 22:16:55 -0700228 return result;
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700229 }
230
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700231 private static boolean equal(Object a, Object b) {
232 return a == b || (a != null && a.equals(b));
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700233 }
234
235 public void dump(String prefix, PrintWriter pw) {
236 pw.print(prefix);
237 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
238 for (int i = 0; i < iface.length; i++) {
239 pw.print(prefix);
240 pw.print(" iface="); pw.print(iface[i]);
241 pw.print(" uid="); pw.print(uid[i]);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700242 pw.print(" tag="); pw.print(tag[i]);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700243 pw.print(" rx="); pw.print(rx[i]);
244 pw.print(" tx="); pw.println(tx[i]);
245 }
246 }
247
248 @Override
249 public String toString() {
250 final CharArrayWriter writer = new CharArrayWriter();
251 dump("", new PrintWriter(writer));
252 return writer.toString();
253 }
254
255 /** {@inheritDoc} */
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700256 public int describeContents() {
257 return 0;
258 }
259
260 /** {@inheritDoc} */
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700261 public void writeToParcel(Parcel dest, int flags) {
262 dest.writeLong(elapsedRealtime);
Jeff Sharkey4a971222011-06-11 22:16:55 -0700263 dest.writeInt(size);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700264 dest.writeStringArray(iface);
265 dest.writeIntArray(uid);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700266 dest.writeIntArray(tag);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700267 dest.writeLongArray(rx);
268 dest.writeLongArray(tx);
269 }
270
271 public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
272 public NetworkStats createFromParcel(Parcel in) {
273 return new NetworkStats(in);
274 }
275
276 public NetworkStats[] newArray(int size) {
277 return new NetworkStats[size];
278 }
279 };
280}