blob: 0e8e7fc5ecfea9d4d529cf6dca87f82b9708f96d [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
Jeff Sharkeya63ba592011-07-19 23:47:12 -070024import com.android.internal.util.Objects;
25
Jeff Sharkey9a13f362011-04-26 16:25:36 -070026import java.io.CharArrayWriter;
27import java.io.PrintWriter;
Jeff Sharkey4a971222011-06-11 22:16:55 -070028import java.util.Arrays;
Jeff Sharkey75279902011-05-24 18:39:45 -070029import java.util.HashSet;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070030
31/**
Jeff Sharkey75279902011-05-24 18:39:45 -070032 * Collection of active network statistics. Can contain summary details across
33 * all interfaces, or details with per-UID granularity. Internally stores data
34 * as a large table, closely matching {@code /proc/} data format. This structure
35 * optimizes for rapid in-memory comparison, but consider using
36 * {@link NetworkStatsHistory} when persisting.
Jeff Sharkey9a13f362011-04-26 16:25:36 -070037 *
38 * @hide
39 */
40public class NetworkStats implements Parcelable {
Jeff Sharkey75279902011-05-24 18:39:45 -070041 /** {@link #iface} value when interface details unavailable. */
Jeff Sharkey9a13f362011-04-26 16:25:36 -070042 public static final String IFACE_ALL = null;
Jeff Sharkey75279902011-05-24 18:39:45 -070043 /** {@link #uid} value when UID details unavailable. */
44 public static final int UID_ALL = -1;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070045 /** {@link #tag} value for without tag. */
46 public static final int TAG_NONE = 0;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070047
Jeff Sharkey9a13f362011-04-26 16:25:36 -070048 /**
49 * {@link SystemClock#elapsedRealtime()} timestamp when this data was
50 * generated.
51 */
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070052 private final long elapsedRealtime;
53 private int size;
54 private String[] iface;
55 private int[] uid;
56 private int[] tag;
57 private long[] rxBytes;
58 private long[] rxPackets;
59 private long[] txBytes;
60 private long[] txPackets;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070061 private int[] operations;
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070062
63 public static class Entry {
64 public String iface;
65 public int uid;
66 public int tag;
67 public long rxBytes;
68 public long rxPackets;
69 public long txBytes;
70 public long txPackets;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070071 public int operations;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070072
73 public Entry() {
74 }
75
76 public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes,
Jeff Sharkeya63ba592011-07-19 23:47:12 -070077 long txPackets, int operations) {
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070078 this.iface = iface;
79 this.uid = uid;
80 this.tag = tag;
81 this.rxBytes = rxBytes;
82 this.rxPackets = rxPackets;
83 this.txBytes = txBytes;
84 this.txPackets = txPackets;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070085 this.operations = operations;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070086 }
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070087 }
Jeff Sharkey9a13f362011-04-26 16:25:36 -070088
Jeff Sharkey4a971222011-06-11 22:16:55 -070089 public NetworkStats(long elapsedRealtime, int initialSize) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -070090 this.elapsedRealtime = elapsedRealtime;
Jeff Sharkey4a971222011-06-11 22:16:55 -070091 this.size = 0;
92 this.iface = new String[initialSize];
93 this.uid = new int[initialSize];
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070094 this.tag = new int[initialSize];
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070095 this.rxBytes = new long[initialSize];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070096 this.rxPackets = new long[initialSize];
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070097 this.txBytes = new long[initialSize];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070098 this.txPackets = new long[initialSize];
Jeff Sharkeya63ba592011-07-19 23:47:12 -070099 this.operations = new int[initialSize];
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700100 }
101
102 public NetworkStats(Parcel parcel) {
103 elapsedRealtime = parcel.readLong();
Jeff Sharkey4a971222011-06-11 22:16:55 -0700104 size = parcel.readInt();
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700105 iface = parcel.createStringArray();
106 uid = parcel.createIntArray();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700107 tag = parcel.createIntArray();
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700108 rxBytes = parcel.createLongArray();
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700109 rxPackets = parcel.createLongArray();
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700110 txBytes = parcel.createLongArray();
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700111 txPackets = parcel.createLongArray();
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700112 operations = parcel.createIntArray();
113 }
114
115 /** {@inheritDoc} */
116 public void writeToParcel(Parcel dest, int flags) {
117 dest.writeLong(elapsedRealtime);
118 dest.writeInt(size);
119 dest.writeStringArray(iface);
120 dest.writeIntArray(uid);
121 dest.writeIntArray(tag);
122 dest.writeLongArray(rxBytes);
123 dest.writeLongArray(rxPackets);
124 dest.writeLongArray(txBytes);
125 dest.writeLongArray(txPackets);
126 dest.writeIntArray(operations);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700127 }
128
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700129 public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
130 long txBytes, long txPackets) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700131 return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0);
132 }
133
134 public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
135 long txBytes, long txPackets, int operations) {
136 return addValues(
137 new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700138 }
139
140 /**
141 * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
142 * object can be recycled across multiple calls.
143 */
144 public NetworkStats addValues(Entry entry) {
Jeff Sharkey4a971222011-06-11 22:16:55 -0700145 if (size >= this.iface.length) {
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700146 final int newLength = Math.max(iface.length, 10) * 3 / 2;
147 iface = Arrays.copyOf(iface, newLength);
148 uid = Arrays.copyOf(uid, newLength);
149 tag = Arrays.copyOf(tag, newLength);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700150 rxBytes = Arrays.copyOf(rxBytes, newLength);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700151 rxPackets = Arrays.copyOf(rxPackets, newLength);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700152 txBytes = Arrays.copyOf(txBytes, newLength);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700153 txPackets = Arrays.copyOf(txPackets, newLength);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700154 operations = Arrays.copyOf(operations, newLength);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700155 }
156
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700157 iface[size] = entry.iface;
158 uid[size] = entry.uid;
159 tag[size] = entry.tag;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700160 rxBytes[size] = entry.rxBytes;
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700161 rxPackets[size] = entry.rxPackets;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700162 txBytes[size] = entry.txBytes;
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700163 txPackets[size] = entry.txPackets;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700164 operations[size] = entry.operations;
Jeff Sharkey4a971222011-06-11 22:16:55 -0700165 size++;
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700166
Jeff Sharkey4a971222011-06-11 22:16:55 -0700167 return this;
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700168 }
169
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700170 /**
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700171 * Return specific stats entry.
172 */
173 public Entry getValues(int i, Entry recycle) {
174 final Entry entry = recycle != null ? recycle : new Entry();
175 entry.iface = iface[i];
176 entry.uid = uid[i];
177 entry.tag = tag[i];
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700178 entry.rxBytes = rxBytes[i];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700179 entry.rxPackets = rxPackets[i];
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700180 entry.txBytes = txBytes[i];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700181 entry.txPackets = txPackets[i];
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700182 entry.operations = operations[i];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700183 return entry;
184 }
185
186 public long getElapsedRealtime() {
187 return elapsedRealtime;
188 }
189
190 public int size() {
191 return size;
192 }
193
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700194 // @VisibleForTesting
195 public int internalSize() {
196 return iface.length;
197 }
198
199 public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700200 long txBytes, long txPackets, int operations) {
201 return combineValues(
202 new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700203 }
204
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700205 /**
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700206 * Combine given values with an existing row, or create a new row if
207 * {@link #findIndex(String, int, int)} is unable to find match. Can also be
208 * used to subtract values from existing rows.
209 */
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700210 public NetworkStats combineValues(Entry entry) {
211 final int i = findIndex(entry.iface, entry.uid, entry.tag);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700212 if (i == -1) {
213 // only create new entry when positive contribution
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700214 addValues(entry);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700215 } else {
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700216 rxBytes[i] += entry.rxBytes;
217 rxPackets[i] += entry.rxPackets;
218 txBytes[i] += entry.txBytes;
219 txPackets[i] += entry.txPackets;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700220 operations[i] += entry.operations;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700221 }
222 return this;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700223 }
224
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700225 /**
226 * Find first stats index that matches the requested parameters.
227 */
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700228 public int findIndex(String iface, int uid, int tag) {
Jeff Sharkey4a971222011-06-11 22:16:55 -0700229 for (int i = 0; i < size; i++) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700230 if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700231 return i;
232 }
233 }
234 return -1;
235 }
236
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700237 /**
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700238 * Splice in {@link #operations} from the given {@link NetworkStats} based
239 * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
240 * since operation counts are at data layer.
241 */
242 public void spliceOperationsFrom(NetworkStats stats) {
243 for (int i = 0; i < size; i++) {
244 final int j = stats.findIndex(IFACE_ALL, uid[i], tag[i]);
245 if (j == -1) {
246 operations[i] = 0;
247 } else {
248 operations[i] = stats.operations[j];
249 }
250 }
251 }
252
253 /**
Jeff Sharkey75279902011-05-24 18:39:45 -0700254 * Return list of unique interfaces known by this data structure.
255 */
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700256 public String[] getUniqueIfaces() {
Jeff Sharkey75279902011-05-24 18:39:45 -0700257 final HashSet<String> ifaces = new HashSet<String>();
258 for (String iface : this.iface) {
259 if (iface != IFACE_ALL) {
260 ifaces.add(iface);
261 }
262 }
263 return ifaces.toArray(new String[ifaces.size()]);
264 }
265
266 /**
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700267 * Return list of unique UIDs known by this data structure.
268 */
269 public int[] getUniqueUids() {
270 final SparseBooleanArray uids = new SparseBooleanArray();
271 for (int uid : this.uid) {
272 uids.put(uid, true);
273 }
274
275 final int size = uids.size();
276 final int[] result = new int[size];
277 for (int i = 0; i < size; i++) {
278 result[i] = uids.keyAt(i);
279 }
280 return result;
281 }
282
283 /**
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700284 * Subtract the given {@link NetworkStats}, effectively leaving the delta
285 * between two snapshots in time. Assumes that statistics rows collect over
286 * time, and that none of them have disappeared.
Jeff Sharkey75279902011-05-24 18:39:45 -0700287 *
Jeff Sharkey3f391352011-06-05 17:42:53 -0700288 * @throws IllegalArgumentException when given {@link NetworkStats} is
289 * non-monotonic.
290 */
291 public NetworkStats subtract(NetworkStats value) {
292 return subtract(value, true, false);
293 }
294
295 /**
296 * Subtract the given {@link NetworkStats}, effectively leaving the delta
297 * between two snapshots in time. Assumes that statistics rows collect over
298 * time, and that none of them have disappeared.
299 * <p>
300 * Instead of throwing when counters are non-monotonic, this variant clamps
301 * results to never be negative.
302 */
303 public NetworkStats subtractClamped(NetworkStats value) {
304 return subtract(value, false, true);
305 }
306
307 /**
308 * Subtract the given {@link NetworkStats}, effectively leaving the delta
309 * between two snapshots in time. Assumes that statistics rows collect over
310 * time, and that none of them have disappeared.
311 *
Jeff Sharkey75279902011-05-24 18:39:45 -0700312 * @param enforceMonotonic Validate that incoming value is strictly
313 * monotonic compared to this object.
Jeff Sharkey3f391352011-06-05 17:42:53 -0700314 * @param clampNegative Instead of throwing like {@code enforceMonotonic},
315 * clamp resulting counters at 0 to prevent negative values.
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700316 */
Jeff Sharkey3f391352011-06-05 17:42:53 -0700317 private NetworkStats subtract(
318 NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700319 final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
320 if (enforceMonotonic && deltaRealtime < 0) {
321 throw new IllegalArgumentException("found non-monotonic realtime");
322 }
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700323
Jeff Sharkey75279902011-05-24 18:39:45 -0700324 // result will have our rows, and elapsed time between snapshots
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700325 final Entry entry = new Entry();
Jeff Sharkey4a971222011-06-11 22:16:55 -0700326 final NetworkStats result = new NetworkStats(deltaRealtime, size);
327 for (int i = 0; i < size; i++) {
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700328 entry.iface = iface[i];
329 entry.uid = uid[i];
330 entry.tag = tag[i];
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700331
332 // find remote row that matches, and subtract
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700333 final int j = value.findIndex(entry.iface, entry.uid, entry.tag);
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700334 if (j == -1) {
335 // newly appearing row, return entire value
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700336 entry.rxBytes = rxBytes[i];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700337 entry.rxPackets = rxPackets[i];
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700338 entry.txBytes = txBytes[i];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700339 entry.txPackets = txPackets[i];
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700340 entry.operations = operations[i];
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700341 } else {
342 // existing row, subtract remote value
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700343 entry.rxBytes = rxBytes[i] - value.rxBytes[j];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700344 entry.rxPackets = rxPackets[i] - value.rxPackets[j];
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700345 entry.txBytes = txBytes[i] - value.txBytes[j];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700346 entry.txPackets = txPackets[i] - value.txPackets[j];
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700347 entry.operations = operations[i] - value.operations[j];
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700348 if (enforceMonotonic
349 && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700350 || entry.txPackets < 0 || entry.operations < 0)) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700351 throw new IllegalArgumentException("found non-monotonic values");
352 }
Jeff Sharkey3f391352011-06-05 17:42:53 -0700353 if (clampNegative) {
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700354 entry.rxBytes = Math.max(0, entry.rxBytes);
355 entry.rxPackets = Math.max(0, entry.rxPackets);
356 entry.txBytes = Math.max(0, entry.txBytes);
357 entry.txPackets = Math.max(0, entry.txPackets);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700358 entry.operations = Math.max(0, entry.operations);
Jeff Sharkey3f391352011-06-05 17:42:53 -0700359 }
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700360 }
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700361
362 result.addValues(entry);
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700363 }
364
Jeff Sharkey4a971222011-06-11 22:16:55 -0700365 return result;
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700366 }
367
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700368 public void dump(String prefix, PrintWriter pw) {
369 pw.print(prefix);
370 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700371 for (int i = 0; i < size; i++) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700372 pw.print(prefix);
373 pw.print(" iface="); pw.print(iface[i]);
374 pw.print(" uid="); pw.print(uid[i]);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700375 pw.print(" tag=0x"); pw.print(Integer.toHexString(tag[i]));
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700376 pw.print(" rxBytes="); pw.print(rxBytes[i]);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700377 pw.print(" rxPackets="); pw.print(rxPackets[i]);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700378 pw.print(" txBytes="); pw.print(txBytes[i]);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700379 pw.print(" txPackets="); pw.print(txPackets[i]);
380 pw.print(" operations="); pw.println(operations[i]);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700381 }
382 }
383
384 @Override
385 public String toString() {
386 final CharArrayWriter writer = new CharArrayWriter();
387 dump("", new PrintWriter(writer));
388 return writer.toString();
389 }
390
391 /** {@inheritDoc} */
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700392 public int describeContents() {
393 return 0;
394 }
395
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700396 public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
397 public NetworkStats createFromParcel(Parcel in) {
398 return new NetworkStats(in);
399 }
400
401 public NetworkStats[] newArray(int size) {
402 return new NetworkStats[size];
403 }
404 };
405}