• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.net;
18 
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 import android.os.SystemClock;
22 import android.util.SparseBooleanArray;
23 
24 import com.android.internal.util.Objects;
25 
26 import java.io.CharArrayWriter;
27 import java.io.PrintWriter;
28 import java.util.Arrays;
29 import java.util.HashSet;
30 
31 /**
32  * 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.
37  *
38  * @hide
39  */
40 public class NetworkStats implements Parcelable {
41     /** {@link #iface} value when interface details unavailable. */
42     public static final String IFACE_ALL = null;
43     /** {@link #uid} value when UID details unavailable. */
44     public static final int UID_ALL = -1;
45     /** {@link #set} value when all sets combined. */
46     public static final int SET_ALL = -1;
47     /** {@link #set} value where background data is accounted. */
48     public static final int SET_DEFAULT = 0;
49     /** {@link #set} value where foreground data is accounted. */
50     public static final int SET_FOREGROUND = 1;
51     /** {@link #tag} value for total data across all tags. */
52     public static final int TAG_NONE = 0;
53 
54     // TODO: move fields to "mVariable" notation
55 
56     /**
57      * {@link SystemClock#elapsedRealtime()} timestamp when this data was
58      * generated.
59      */
60     private final long elapsedRealtime;
61     private int size;
62     private String[] iface;
63     private int[] uid;
64     private int[] set;
65     private int[] tag;
66     private long[] rxBytes;
67     private long[] rxPackets;
68     private long[] txBytes;
69     private long[] txPackets;
70     private long[] operations;
71 
72     public static class Entry {
73         public String iface;
74         public int uid;
75         public int set;
76         public int tag;
77         public long rxBytes;
78         public long rxPackets;
79         public long txBytes;
80         public long txPackets;
81         public long operations;
82 
Entry()83         public Entry() {
84             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
85         }
86 
Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)87         public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
88             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
89                     operations);
90         }
91 
Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)92         public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
93                 long txBytes, long txPackets, long operations) {
94             this.iface = iface;
95             this.uid = uid;
96             this.set = set;
97             this.tag = tag;
98             this.rxBytes = rxBytes;
99             this.rxPackets = rxPackets;
100             this.txBytes = txBytes;
101             this.txPackets = txPackets;
102             this.operations = operations;
103         }
104 
isNegative()105         public boolean isNegative() {
106             return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
107         }
108 
isEmpty()109         public boolean isEmpty() {
110             return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
111                     && operations == 0;
112         }
113 
add(Entry another)114         public void add(Entry another) {
115             this.rxBytes += another.rxBytes;
116             this.rxPackets += another.rxPackets;
117             this.txBytes += another.txBytes;
118             this.txPackets += another.txPackets;
119             this.operations += another.operations;
120         }
121 
122         @Override
toString()123         public String toString() {
124             final StringBuilder builder = new StringBuilder();
125             builder.append("iface=").append(iface);
126             builder.append(" uid=").append(uid);
127             builder.append(" set=").append(setToString(set));
128             builder.append(" tag=").append(tagToString(tag));
129             builder.append(" rxBytes=").append(rxBytes);
130             builder.append(" rxPackets=").append(rxPackets);
131             builder.append(" txBytes=").append(txBytes);
132             builder.append(" txPackets=").append(txPackets);
133             builder.append(" operations=").append(operations);
134             return builder.toString();
135         }
136     }
137 
NetworkStats(long elapsedRealtime, int initialSize)138     public NetworkStats(long elapsedRealtime, int initialSize) {
139         this.elapsedRealtime = elapsedRealtime;
140         this.size = 0;
141         this.iface = new String[initialSize];
142         this.uid = new int[initialSize];
143         this.set = new int[initialSize];
144         this.tag = new int[initialSize];
145         this.rxBytes = new long[initialSize];
146         this.rxPackets = new long[initialSize];
147         this.txBytes = new long[initialSize];
148         this.txPackets = new long[initialSize];
149         this.operations = new long[initialSize];
150     }
151 
NetworkStats(Parcel parcel)152     public NetworkStats(Parcel parcel) {
153         elapsedRealtime = parcel.readLong();
154         size = parcel.readInt();
155         iface = parcel.createStringArray();
156         uid = parcel.createIntArray();
157         set = parcel.createIntArray();
158         tag = parcel.createIntArray();
159         rxBytes = parcel.createLongArray();
160         rxPackets = parcel.createLongArray();
161         txBytes = parcel.createLongArray();
162         txPackets = parcel.createLongArray();
163         operations = parcel.createLongArray();
164     }
165 
166     @Override
writeToParcel(Parcel dest, int flags)167     public void writeToParcel(Parcel dest, int flags) {
168         dest.writeLong(elapsedRealtime);
169         dest.writeInt(size);
170         dest.writeStringArray(iface);
171         dest.writeIntArray(uid);
172         dest.writeIntArray(set);
173         dest.writeIntArray(tag);
174         dest.writeLongArray(rxBytes);
175         dest.writeLongArray(rxPackets);
176         dest.writeLongArray(txBytes);
177         dest.writeLongArray(txPackets);
178         dest.writeLongArray(operations);
179     }
180 
181     @Override
clone()182     public NetworkStats clone() {
183         final NetworkStats clone = new NetworkStats(elapsedRealtime, size);
184         NetworkStats.Entry entry = null;
185         for (int i = 0; i < size; i++) {
186             entry = getValues(i, entry);
187             clone.addValues(entry);
188         }
189         return clone;
190     }
191 
192     // @VisibleForTesting
addIfaceValues( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets)193     public NetworkStats addIfaceValues(
194             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
195         return addValues(
196                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
197     }
198 
199     // @VisibleForTesting
addValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)200     public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes,
201             long rxPackets, long txBytes, long txPackets, long operations) {
202         return addValues(new Entry(
203                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
204     }
205 
206     /**
207      * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
208      * object can be recycled across multiple calls.
209      */
addValues(Entry entry)210     public NetworkStats addValues(Entry entry) {
211         if (size >= this.iface.length) {
212             final int newLength = Math.max(iface.length, 10) * 3 / 2;
213             iface = Arrays.copyOf(iface, newLength);
214             uid = Arrays.copyOf(uid, newLength);
215             set = Arrays.copyOf(set, newLength);
216             tag = Arrays.copyOf(tag, newLength);
217             rxBytes = Arrays.copyOf(rxBytes, newLength);
218             rxPackets = Arrays.copyOf(rxPackets, newLength);
219             txBytes = Arrays.copyOf(txBytes, newLength);
220             txPackets = Arrays.copyOf(txPackets, newLength);
221             operations = Arrays.copyOf(operations, newLength);
222         }
223 
224         iface[size] = entry.iface;
225         uid[size] = entry.uid;
226         set[size] = entry.set;
227         tag[size] = entry.tag;
228         rxBytes[size] = entry.rxBytes;
229         rxPackets[size] = entry.rxPackets;
230         txBytes[size] = entry.txBytes;
231         txPackets[size] = entry.txPackets;
232         operations[size] = entry.operations;
233         size++;
234 
235         return this;
236     }
237 
238     /**
239      * Return specific stats entry.
240      */
getValues(int i, Entry recycle)241     public Entry getValues(int i, Entry recycle) {
242         final Entry entry = recycle != null ? recycle : new Entry();
243         entry.iface = iface[i];
244         entry.uid = uid[i];
245         entry.set = set[i];
246         entry.tag = tag[i];
247         entry.rxBytes = rxBytes[i];
248         entry.rxPackets = rxPackets[i];
249         entry.txBytes = txBytes[i];
250         entry.txPackets = txPackets[i];
251         entry.operations = operations[i];
252         return entry;
253     }
254 
getElapsedRealtime()255     public long getElapsedRealtime() {
256         return elapsedRealtime;
257     }
258 
259     /**
260      * Return age of this {@link NetworkStats} object with respect to
261      * {@link SystemClock#elapsedRealtime()}.
262      */
getElapsedRealtimeAge()263     public long getElapsedRealtimeAge() {
264         return SystemClock.elapsedRealtime() - elapsedRealtime;
265     }
266 
size()267     public int size() {
268         return size;
269     }
270 
271     // @VisibleForTesting
internalSize()272     public int internalSize() {
273         return iface.length;
274     }
275 
276     @Deprecated
combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)277     public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
278             long txBytes, long txPackets, long operations) {
279         return combineValues(
280                 iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes, txPackets, operations);
281     }
282 
combineValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)283     public NetworkStats combineValues(String iface, int uid, int set, int tag, long rxBytes,
284             long rxPackets, long txBytes, long txPackets, long operations) {
285         return combineValues(new Entry(
286                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
287     }
288 
289     /**
290      * Combine given values with an existing row, or create a new row if
291      * {@link #findIndex(String, int, int, int)} is unable to find match. Can
292      * also be used to subtract values from existing rows.
293      */
combineValues(Entry entry)294     public NetworkStats combineValues(Entry entry) {
295         final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag);
296         if (i == -1) {
297             // only create new entry when positive contribution
298             addValues(entry);
299         } else {
300             rxBytes[i] += entry.rxBytes;
301             rxPackets[i] += entry.rxPackets;
302             txBytes[i] += entry.txBytes;
303             txPackets[i] += entry.txPackets;
304             operations[i] += entry.operations;
305         }
306         return this;
307     }
308 
309     /**
310      * Combine all values from another {@link NetworkStats} into this object.
311      */
combineAllValues(NetworkStats another)312     public void combineAllValues(NetworkStats another) {
313         NetworkStats.Entry entry = null;
314         for (int i = 0; i < another.size; i++) {
315             entry = another.getValues(i, entry);
316             combineValues(entry);
317         }
318     }
319 
320     /**
321      * Find first stats index that matches the requested parameters.
322      */
findIndex(String iface, int uid, int set, int tag)323     public int findIndex(String iface, int uid, int set, int tag) {
324         for (int i = 0; i < size; i++) {
325             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
326                     && Objects.equal(iface, this.iface[i])) {
327                 return i;
328             }
329         }
330         return -1;
331     }
332 
333     /**
334      * Find first stats index that matches the requested parameters, starting
335      * search around the hinted index as an optimization.
336      */
337     // @VisibleForTesting
findIndexHinted(String iface, int uid, int set, int tag, int hintIndex)338     public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) {
339         for (int offset = 0; offset < size; offset++) {
340             final int halfOffset = offset / 2;
341 
342             // search outwards from hint index, alternating forward and backward
343             final int i;
344             if (offset % 2 == 0) {
345                 i = (hintIndex + halfOffset) % size;
346             } else {
347                 i = (size + hintIndex - halfOffset - 1) % size;
348             }
349 
350             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
351                     && Objects.equal(iface, this.iface[i])) {
352                 return i;
353             }
354         }
355         return -1;
356     }
357 
358     /**
359      * Splice in {@link #operations} from the given {@link NetworkStats} based
360      * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
361      * since operation counts are at data layer.
362      */
spliceOperationsFrom(NetworkStats stats)363     public void spliceOperationsFrom(NetworkStats stats) {
364         for (int i = 0; i < size; i++) {
365             final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i]);
366             if (j == -1) {
367                 operations[i] = 0;
368             } else {
369                 operations[i] = stats.operations[j];
370             }
371         }
372     }
373 
374     /**
375      * Return list of unique interfaces known by this data structure.
376      */
getUniqueIfaces()377     public String[] getUniqueIfaces() {
378         final HashSet<String> ifaces = new HashSet<String>();
379         for (String iface : this.iface) {
380             if (iface != IFACE_ALL) {
381                 ifaces.add(iface);
382             }
383         }
384         return ifaces.toArray(new String[ifaces.size()]);
385     }
386 
387     /**
388      * Return list of unique UIDs known by this data structure.
389      */
getUniqueUids()390     public int[] getUniqueUids() {
391         final SparseBooleanArray uids = new SparseBooleanArray();
392         for (int uid : this.uid) {
393             uids.put(uid, true);
394         }
395 
396         final int size = uids.size();
397         final int[] result = new int[size];
398         for (int i = 0; i < size; i++) {
399             result[i] = uids.keyAt(i);
400         }
401         return result;
402     }
403 
404     /**
405      * Return total bytes represented by this snapshot object, usually used when
406      * checking if a {@link #subtract(NetworkStats)} delta passes a threshold.
407      */
getTotalBytes()408     public long getTotalBytes() {
409         final Entry entry = getTotal(null);
410         return entry.rxBytes + entry.txBytes;
411     }
412 
413     /**
414      * Return total of all fields represented by this snapshot object.
415      */
getTotal(Entry recycle)416     public Entry getTotal(Entry recycle) {
417         return getTotal(recycle, null, UID_ALL, false);
418     }
419 
420     /**
421      * Return total of all fields represented by this snapshot object matching
422      * the requested {@link #uid}.
423      */
getTotal(Entry recycle, int limitUid)424     public Entry getTotal(Entry recycle, int limitUid) {
425         return getTotal(recycle, null, limitUid, false);
426     }
427 
428     /**
429      * Return total of all fields represented by this snapshot object matching
430      * the requested {@link #iface}.
431      */
getTotal(Entry recycle, HashSet<String> limitIface)432     public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
433         return getTotal(recycle, limitIface, UID_ALL, false);
434     }
435 
getTotalIncludingTags(Entry recycle)436     public Entry getTotalIncludingTags(Entry recycle) {
437         return getTotal(recycle, null, UID_ALL, true);
438     }
439 
440     /**
441      * Return total of all fields represented by this snapshot object matching
442      * the requested {@link #iface} and {@link #uid}.
443      *
444      * @param limitIface Set of {@link #iface} to include in total; or {@code
445      *            null} to include all ifaces.
446      */
getTotal( Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags)447     private Entry getTotal(
448             Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) {
449         final Entry entry = recycle != null ? recycle : new Entry();
450 
451         entry.iface = IFACE_ALL;
452         entry.uid = limitUid;
453         entry.set = SET_ALL;
454         entry.tag = TAG_NONE;
455         entry.rxBytes = 0;
456         entry.rxPackets = 0;
457         entry.txBytes = 0;
458         entry.txPackets = 0;
459         entry.operations = 0;
460 
461         for (int i = 0; i < size; i++) {
462             final boolean matchesUid = (limitUid == UID_ALL) || (limitUid == uid[i]);
463             final boolean matchesIface = (limitIface == null) || (limitIface.contains(iface[i]));
464 
465             if (matchesUid && matchesIface) {
466                 // skip specific tags, since already counted in TAG_NONE
467                 if (tag[i] != TAG_NONE && !includeTags) continue;
468 
469                 entry.rxBytes += rxBytes[i];
470                 entry.rxPackets += rxPackets[i];
471                 entry.txBytes += txBytes[i];
472                 entry.txPackets += txPackets[i];
473                 entry.operations += operations[i];
474             }
475         }
476         return entry;
477     }
478 
479     /**
480      * Subtract the given {@link NetworkStats}, effectively leaving the delta
481      * between two snapshots in time. Assumes that statistics rows collect over
482      * time, and that none of them have disappeared.
483      */
subtract(NetworkStats right)484     public NetworkStats subtract(NetworkStats right) {
485         return subtract(this, right, null, null);
486     }
487 
488     /**
489      * Subtract the two given {@link NetworkStats} objects, returning the delta
490      * between two snapshots in time. Assumes that statistics rows collect over
491      * time, and that none of them have disappeared.
492      * <p>
493      * If counters have rolled backwards, they are clamped to {@code 0} and
494      * reported to the given {@link NonMonotonicObserver}.
495      */
subtract( NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie)496     public static <C> NetworkStats subtract(
497             NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie) {
498         long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
499         if (deltaRealtime < 0) {
500             if (observer != null) {
501                 observer.foundNonMonotonic(left, -1, right, -1, cookie);
502             }
503             deltaRealtime = 0;
504         }
505 
506         // result will have our rows, and elapsed time between snapshots
507         final Entry entry = new Entry();
508         final NetworkStats result = new NetworkStats(deltaRealtime, left.size);
509         for (int i = 0; i < left.size; i++) {
510             entry.iface = left.iface[i];
511             entry.uid = left.uid[i];
512             entry.set = left.set[i];
513             entry.tag = left.tag[i];
514 
515             // find remote row that matches, and subtract
516             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, i);
517             if (j == -1) {
518                 // newly appearing row, return entire value
519                 entry.rxBytes = left.rxBytes[i];
520                 entry.rxPackets = left.rxPackets[i];
521                 entry.txBytes = left.txBytes[i];
522                 entry.txPackets = left.txPackets[i];
523                 entry.operations = left.operations[i];
524             } else {
525                 // existing row, subtract remote value
526                 entry.rxBytes = left.rxBytes[i] - right.rxBytes[j];
527                 entry.rxPackets = left.rxPackets[i] - right.rxPackets[j];
528                 entry.txBytes = left.txBytes[i] - right.txBytes[j];
529                 entry.txPackets = left.txPackets[i] - right.txPackets[j];
530                 entry.operations = left.operations[i] - right.operations[j];
531 
532                 if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
533                         || entry.txPackets < 0 || entry.operations < 0) {
534                     if (observer != null) {
535                         observer.foundNonMonotonic(left, i, right, j, cookie);
536                     }
537                     entry.rxBytes = Math.max(entry.rxBytes, 0);
538                     entry.rxPackets = Math.max(entry.rxPackets, 0);
539                     entry.txBytes = Math.max(entry.txBytes, 0);
540                     entry.txPackets = Math.max(entry.txPackets, 0);
541                     entry.operations = Math.max(entry.operations, 0);
542                 }
543             }
544 
545             result.addValues(entry);
546         }
547 
548         return result;
549     }
550 
551     /**
552      * Return total statistics grouped by {@link #iface}; doesn't mutate the
553      * original structure.
554      */
groupedByIface()555     public NetworkStats groupedByIface() {
556         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
557 
558         final Entry entry = new Entry();
559         entry.uid = UID_ALL;
560         entry.set = SET_ALL;
561         entry.tag = TAG_NONE;
562         entry.operations = 0L;
563 
564         for (int i = 0; i < size; i++) {
565             // skip specific tags, since already counted in TAG_NONE
566             if (tag[i] != TAG_NONE) continue;
567 
568             entry.iface = iface[i];
569             entry.rxBytes = rxBytes[i];
570             entry.rxPackets = rxPackets[i];
571             entry.txBytes = txBytes[i];
572             entry.txPackets = txPackets[i];
573             stats.combineValues(entry);
574         }
575 
576         return stats;
577     }
578 
579     /**
580      * Return total statistics grouped by {@link #uid}; doesn't mutate the
581      * original structure.
582      */
groupedByUid()583     public NetworkStats groupedByUid() {
584         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
585 
586         final Entry entry = new Entry();
587         entry.iface = IFACE_ALL;
588         entry.set = SET_ALL;
589         entry.tag = TAG_NONE;
590 
591         for (int i = 0; i < size; i++) {
592             // skip specific tags, since already counted in TAG_NONE
593             if (tag[i] != TAG_NONE) continue;
594 
595             entry.uid = uid[i];
596             entry.rxBytes = rxBytes[i];
597             entry.rxPackets = rxPackets[i];
598             entry.txBytes = txBytes[i];
599             entry.txPackets = txPackets[i];
600             entry.operations = operations[i];
601             stats.combineValues(entry);
602         }
603 
604         return stats;
605     }
606 
607     /**
608      * Return all rows except those attributed to the requested UID; doesn't
609      * mutate the original structure.
610      */
withoutUid(int uid)611     public NetworkStats withoutUid(int uid) {
612         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
613 
614         Entry entry = new Entry();
615         for (int i = 0; i < size; i++) {
616             entry = getValues(i, entry);
617             if (entry.uid != uid) {
618                 stats.addValues(entry);
619             }
620         }
621 
622         return stats;
623     }
624 
dump(String prefix, PrintWriter pw)625     public void dump(String prefix, PrintWriter pw) {
626         pw.print(prefix);
627         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
628         for (int i = 0; i < size; i++) {
629             pw.print(prefix);
630             pw.print("  ["); pw.print(i); pw.print("]");
631             pw.print(" iface="); pw.print(iface[i]);
632             pw.print(" uid="); pw.print(uid[i]);
633             pw.print(" set="); pw.print(setToString(set[i]));
634             pw.print(" tag="); pw.print(tagToString(tag[i]));
635             pw.print(" rxBytes="); pw.print(rxBytes[i]);
636             pw.print(" rxPackets="); pw.print(rxPackets[i]);
637             pw.print(" txBytes="); pw.print(txBytes[i]);
638             pw.print(" txPackets="); pw.print(txPackets[i]);
639             pw.print(" operations="); pw.println(operations[i]);
640         }
641     }
642 
643     /**
644      * Return text description of {@link #set} value.
645      */
setToString(int set)646     public static String setToString(int set) {
647         switch (set) {
648             case SET_ALL:
649                 return "ALL";
650             case SET_DEFAULT:
651                 return "DEFAULT";
652             case SET_FOREGROUND:
653                 return "FOREGROUND";
654             default:
655                 return "UNKNOWN";
656         }
657     }
658 
659     /**
660      * Return text description of {@link #tag} value.
661      */
tagToString(int tag)662     public static String tagToString(int tag) {
663         return "0x" + Integer.toHexString(tag);
664     }
665 
666     @Override
toString()667     public String toString() {
668         final CharArrayWriter writer = new CharArrayWriter();
669         dump("", new PrintWriter(writer));
670         return writer.toString();
671     }
672 
673     @Override
describeContents()674     public int describeContents() {
675         return 0;
676     }
677 
678     public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
679         @Override
680         public NetworkStats createFromParcel(Parcel in) {
681             return new NetworkStats(in);
682         }
683 
684         @Override
685         public NetworkStats[] newArray(int size) {
686             return new NetworkStats[size];
687         }
688     };
689 
690     public interface NonMonotonicObserver<C> {
foundNonMonotonic( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie)691         public void foundNonMonotonic(
692                 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
693     }
694 }
695