NetworkStats.java revision b6a920124f28422877f59bfb32719099a0067d76
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.Slog;
23import android.util.SparseBooleanArray;
24
25import com.android.internal.annotations.VisibleForTesting;
26import com.android.internal.util.ArrayUtils;
27
28import libcore.util.EmptyArray;
29
30import java.io.CharArrayWriter;
31import java.io.PrintWriter;
32import java.util.Arrays;
33import java.util.HashSet;
34import java.util.Objects;
35
36/**
37 * Collection of active network statistics. Can contain summary details across
38 * all interfaces, or details with per-UID granularity. Internally stores data
39 * as a large table, closely matching {@code /proc/} data format. This structure
40 * optimizes for rapid in-memory comparison, but consider using
41 * {@link NetworkStatsHistory} when persisting.
42 *
43 * @hide
44 */
45public class NetworkStats implements Parcelable {
46    private static final String TAG = "NetworkStats";
47    /** {@link #iface} value when interface details unavailable. */
48    public static final String IFACE_ALL = null;
49    /** {@link #uid} value when UID details unavailable. */
50    public static final int UID_ALL = -1;
51    /** {@link #tag} value matching any tag. */
52    // TODO: Rename TAG_ALL to TAG_ANY.
53    public static final int TAG_ALL = -1;
54    /** {@link #set} value for all sets combined, not including debug sets. */
55    public static final int SET_ALL = -1;
56    /** {@link #set} value where background data is accounted. */
57    public static final int SET_DEFAULT = 0;
58    /** {@link #set} value where foreground data is accounted. */
59    public static final int SET_FOREGROUND = 1;
60    /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */
61    public static final int SET_DEBUG_START = 1000;
62    /** Debug {@link #set} value when the VPN stats are moved in. */
63    public static final int SET_DBG_VPN_IN = 1001;
64    /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
65    public static final int SET_DBG_VPN_OUT = 1002;
66
67    /** Include all interfaces when filtering */
68    public static final String[] INTERFACES_ALL = null;
69
70    /** {@link #tag} value for total data across all tags. */
71    // TODO: Rename TAG_NONE to TAG_ALL.
72    public static final int TAG_NONE = 0;
73
74    /** {@link #metered} value to account for all metered states. */
75    public static final int METERED_ALL = -1;
76    /** {@link #metered} value where native, unmetered data is accounted. */
77    public static final int METERED_NO = 0;
78    /** {@link #metered} value where metered data is accounted. */
79    public static final int METERED_YES = 1;
80
81    /** {@link #roaming} value to account for all roaming states. */
82    public static final int ROAMING_ALL = -1;
83    /** {@link #roaming} value where native, non-roaming data is accounted. */
84    public static final int ROAMING_NO = 0;
85    /** {@link #roaming} value where roaming data is accounted. */
86    public static final int ROAMING_YES = 1;
87
88    /** {@link #onDefaultNetwork} value to account for all default network states. */
89    public static final int DEFAULT_NETWORK_ALL = -1;
90    /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
91    public static final int DEFAULT_NETWORK_NO = 0;
92    /** {@link #onDefaultNetwork} value to account for usage while the default network. */
93    public static final int DEFAULT_NETWORK_YES = 1;
94
95    /** Denotes a request for stats at the interface level. */
96    public static final int STATS_PER_IFACE = 0;
97    /** Denotes a request for stats at the interface and UID level. */
98    public static final int STATS_PER_UID = 1;
99
100    // TODO: move fields to "mVariable" notation
101
102    /**
103     * {@link SystemClock#elapsedRealtime()} timestamp when this data was
104     * generated.
105     */
106    private long elapsedRealtime;
107    private int size;
108    private int capacity;
109    private String[] iface;
110    private int[] uid;
111    private int[] set;
112    private int[] tag;
113    private int[] metered;
114    private int[] roaming;
115    private int[] defaultNetwork;
116    private long[] rxBytes;
117    private long[] rxPackets;
118    private long[] txBytes;
119    private long[] txPackets;
120    private long[] operations;
121
122    public static class Entry {
123        public String iface;
124        public int uid;
125        public int set;
126        public int tag;
127        /**
128         * Note that this is only populated w/ the default value when read from /proc or written
129         * to disk. We merge in the correct value when reporting this value to clients of
130         * getSummary().
131         */
132        public int metered;
133        /**
134         * Note that this is only populated w/ the default value when read from /proc or written
135         * to disk. We merge in the correct value when reporting this value to clients of
136         * getSummary().
137         */
138        public int roaming;
139        /**
140         * Note that this is only populated w/ the default value when read from /proc or written
141         * to disk. We merge in the correct value when reporting this value to clients of
142         * getSummary().
143         */
144        public int defaultNetwork;
145        public long rxBytes;
146        public long rxPackets;
147        public long txBytes;
148        public long txPackets;
149        public long operations;
150
151        public Entry() {
152            this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
153        }
154
155        public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
156            this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
157                    operations);
158        }
159
160        public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
161                long txBytes, long txPackets, long operations) {
162            this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
163                    rxBytes, rxPackets, txBytes, txPackets, operations);
164        }
165
166        public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
167                 int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
168                 long operations) {
169            this.iface = iface;
170            this.uid = uid;
171            this.set = set;
172            this.tag = tag;
173            this.metered = metered;
174            this.roaming = roaming;
175            this.defaultNetwork = defaultNetwork;
176            this.rxBytes = rxBytes;
177            this.rxPackets = rxPackets;
178            this.txBytes = txBytes;
179            this.txPackets = txPackets;
180            this.operations = operations;
181        }
182
183        public boolean isNegative() {
184            return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
185        }
186
187        public boolean isEmpty() {
188            return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
189                    && operations == 0;
190        }
191
192        public void add(Entry another) {
193            this.rxBytes += another.rxBytes;
194            this.rxPackets += another.rxPackets;
195            this.txBytes += another.txBytes;
196            this.txPackets += another.txPackets;
197            this.operations += another.operations;
198        }
199
200        @Override
201        public String toString() {
202            final StringBuilder builder = new StringBuilder();
203            builder.append("iface=").append(iface);
204            builder.append(" uid=").append(uid);
205            builder.append(" set=").append(setToString(set));
206            builder.append(" tag=").append(tagToString(tag));
207            builder.append(" metered=").append(meteredToString(metered));
208            builder.append(" roaming=").append(roamingToString(roaming));
209            builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork));
210            builder.append(" rxBytes=").append(rxBytes);
211            builder.append(" rxPackets=").append(rxPackets);
212            builder.append(" txBytes=").append(txBytes);
213            builder.append(" txPackets=").append(txPackets);
214            builder.append(" operations=").append(operations);
215            return builder.toString();
216        }
217
218        @Override
219        public boolean equals(Object o) {
220            if (o instanceof Entry) {
221                final Entry e = (Entry) o;
222                return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
223                        && roaming == e.roaming && defaultNetwork == e.defaultNetwork
224                        && rxBytes == e.rxBytes && rxPackets == e.rxPackets
225                        && txBytes == e.txBytes && txPackets == e.txPackets
226                        && operations == e.operations && iface.equals(e.iface);
227            }
228            return false;
229        }
230
231        @Override
232        public int hashCode() {
233            return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface);
234        }
235    }
236
237    public NetworkStats(long elapsedRealtime, int initialSize) {
238        this.elapsedRealtime = elapsedRealtime;
239        this.size = 0;
240        if (initialSize > 0) {
241            this.capacity = initialSize;
242            this.iface = new String[initialSize];
243            this.uid = new int[initialSize];
244            this.set = new int[initialSize];
245            this.tag = new int[initialSize];
246            this.metered = new int[initialSize];
247            this.roaming = new int[initialSize];
248            this.defaultNetwork = new int[initialSize];
249            this.rxBytes = new long[initialSize];
250            this.rxPackets = new long[initialSize];
251            this.txBytes = new long[initialSize];
252            this.txPackets = new long[initialSize];
253            this.operations = new long[initialSize];
254        } else {
255            // Special case for use by NetworkStatsFactory to start out *really* empty.
256            clear();
257        }
258    }
259
260    public NetworkStats(Parcel parcel) {
261        elapsedRealtime = parcel.readLong();
262        size = parcel.readInt();
263        capacity = parcel.readInt();
264        iface = parcel.createStringArray();
265        uid = parcel.createIntArray();
266        set = parcel.createIntArray();
267        tag = parcel.createIntArray();
268        metered = parcel.createIntArray();
269        roaming = parcel.createIntArray();
270        defaultNetwork = parcel.createIntArray();
271        rxBytes = parcel.createLongArray();
272        rxPackets = parcel.createLongArray();
273        txBytes = parcel.createLongArray();
274        txPackets = parcel.createLongArray();
275        operations = parcel.createLongArray();
276    }
277
278    @Override
279    public void writeToParcel(Parcel dest, int flags) {
280        dest.writeLong(elapsedRealtime);
281        dest.writeInt(size);
282        dest.writeInt(capacity);
283        dest.writeStringArray(iface);
284        dest.writeIntArray(uid);
285        dest.writeIntArray(set);
286        dest.writeIntArray(tag);
287        dest.writeIntArray(metered);
288        dest.writeIntArray(roaming);
289        dest.writeIntArray(defaultNetwork);
290        dest.writeLongArray(rxBytes);
291        dest.writeLongArray(rxPackets);
292        dest.writeLongArray(txBytes);
293        dest.writeLongArray(txPackets);
294        dest.writeLongArray(operations);
295    }
296
297    @Override
298    public NetworkStats clone() {
299        final NetworkStats clone = new NetworkStats(elapsedRealtime, size);
300        NetworkStats.Entry entry = null;
301        for (int i = 0; i < size; i++) {
302            entry = getValues(i, entry);
303            clone.addValues(entry);
304        }
305        return clone;
306    }
307
308    /**
309     * Clear all data stored in this object.
310     */
311    public void clear() {
312        this.capacity = 0;
313        this.iface = EmptyArray.STRING;
314        this.uid = EmptyArray.INT;
315        this.set = EmptyArray.INT;
316        this.tag = EmptyArray.INT;
317        this.metered = EmptyArray.INT;
318        this.roaming = EmptyArray.INT;
319        this.defaultNetwork = EmptyArray.INT;
320        this.rxBytes = EmptyArray.LONG;
321        this.rxPackets = EmptyArray.LONG;
322        this.txBytes = EmptyArray.LONG;
323        this.txPackets = EmptyArray.LONG;
324        this.operations = EmptyArray.LONG;
325    }
326
327    @VisibleForTesting
328    public NetworkStats addIfaceValues(
329            String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
330        return addValues(
331                iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
332    }
333
334    @VisibleForTesting
335    public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes,
336            long rxPackets, long txBytes, long txPackets, long operations) {
337        return addValues(new Entry(
338                iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
339    }
340
341    @VisibleForTesting
342    public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming,
343            int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
344            long operations) {
345        return addValues(new Entry(
346                iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
347                txBytes, txPackets, operations));
348    }
349
350    /**
351     * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
352     * object can be recycled across multiple calls.
353     */
354    public NetworkStats addValues(Entry entry) {
355        if (size >= capacity) {
356            final int newLength = Math.max(size, 10) * 3 / 2;
357            iface = Arrays.copyOf(iface, newLength);
358            uid = Arrays.copyOf(uid, newLength);
359            set = Arrays.copyOf(set, newLength);
360            tag = Arrays.copyOf(tag, newLength);
361            metered = Arrays.copyOf(metered, newLength);
362            roaming = Arrays.copyOf(roaming, newLength);
363            defaultNetwork = Arrays.copyOf(defaultNetwork, newLength);
364            rxBytes = Arrays.copyOf(rxBytes, newLength);
365            rxPackets = Arrays.copyOf(rxPackets, newLength);
366            txBytes = Arrays.copyOf(txBytes, newLength);
367            txPackets = Arrays.copyOf(txPackets, newLength);
368            operations = Arrays.copyOf(operations, newLength);
369            capacity = newLength;
370        }
371
372        setValues(size, entry);
373        size++;
374
375        return this;
376    }
377
378    private void setValues(int i, Entry entry) {
379        iface[i] = entry.iface;
380        uid[i] = entry.uid;
381        set[i] = entry.set;
382        tag[i] = entry.tag;
383        metered[i] = entry.metered;
384        roaming[i] = entry.roaming;
385        defaultNetwork[i] = entry.defaultNetwork;
386        rxBytes[i] = entry.rxBytes;
387        rxPackets[i] = entry.rxPackets;
388        txBytes[i] = entry.txBytes;
389        txPackets[i] = entry.txPackets;
390        operations[i] = entry.operations;
391    }
392
393    /**
394     * Return specific stats entry.
395     */
396    public Entry getValues(int i, Entry recycle) {
397        final Entry entry = recycle != null ? recycle : new Entry();
398        entry.iface = iface[i];
399        entry.uid = uid[i];
400        entry.set = set[i];
401        entry.tag = tag[i];
402        entry.metered = metered[i];
403        entry.roaming = roaming[i];
404        entry.defaultNetwork = defaultNetwork[i];
405        entry.rxBytes = rxBytes[i];
406        entry.rxPackets = rxPackets[i];
407        entry.txBytes = txBytes[i];
408        entry.txPackets = txPackets[i];
409        entry.operations = operations[i];
410        return entry;
411    }
412
413    public long getElapsedRealtime() {
414        return elapsedRealtime;
415    }
416
417    public void setElapsedRealtime(long time) {
418        elapsedRealtime = time;
419    }
420
421    /**
422     * Return age of this {@link NetworkStats} object with respect to
423     * {@link SystemClock#elapsedRealtime()}.
424     */
425    public long getElapsedRealtimeAge() {
426        return SystemClock.elapsedRealtime() - elapsedRealtime;
427    }
428
429    public int size() {
430        return size;
431    }
432
433    @VisibleForTesting
434    public int internalSize() {
435        return capacity;
436    }
437
438    @Deprecated
439    public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
440            long txBytes, long txPackets, long operations) {
441        return combineValues(
442                iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes,
443                txPackets, operations);
444    }
445
446    public NetworkStats combineValues(String iface, int uid, int set, int tag,
447            long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
448        return combineValues(new Entry(
449                iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
450    }
451
452    /**
453     * Combine given values with an existing row, or create a new row if
454     * {@link #findIndex(String, int, int, int, int)} is unable to find match. Can
455     * also be used to subtract values from existing rows.
456     */
457    public NetworkStats combineValues(Entry entry) {
458        final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered,
459                entry.roaming, entry.defaultNetwork);
460        if (i == -1) {
461            // only create new entry when positive contribution
462            addValues(entry);
463        } else {
464            rxBytes[i] += entry.rxBytes;
465            rxPackets[i] += entry.rxPackets;
466            txBytes[i] += entry.txBytes;
467            txPackets[i] += entry.txPackets;
468            operations[i] += entry.operations;
469        }
470        return this;
471    }
472
473    /**
474     * Combine all values from another {@link NetworkStats} into this object.
475     */
476    public void combineAllValues(NetworkStats another) {
477        NetworkStats.Entry entry = null;
478        for (int i = 0; i < another.size; i++) {
479            entry = another.getValues(i, entry);
480            combineValues(entry);
481        }
482    }
483
484    /**
485     * Find first stats index that matches the requested parameters.
486     */
487    public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming,
488            int defaultNetwork) {
489        for (int i = 0; i < size; i++) {
490            if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
491                    && metered == this.metered[i] && roaming == this.roaming[i]
492                    && defaultNetwork == this.defaultNetwork[i]
493                    && Objects.equals(iface, this.iface[i])) {
494                return i;
495            }
496        }
497        return -1;
498    }
499
500    /**
501     * Find first stats index that matches the requested parameters, starting
502     * search around the hinted index as an optimization.
503     */
504    @VisibleForTesting
505    public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming,
506            int defaultNetwork, int hintIndex) {
507        for (int offset = 0; offset < size; offset++) {
508            final int halfOffset = offset / 2;
509
510            // search outwards from hint index, alternating forward and backward
511            final int i;
512            if (offset % 2 == 0) {
513                i = (hintIndex + halfOffset) % size;
514            } else {
515                i = (size + hintIndex - halfOffset - 1) % size;
516            }
517
518            if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
519                    && metered == this.metered[i] && roaming == this.roaming[i]
520                    && defaultNetwork == this.defaultNetwork[i]
521                    && Objects.equals(iface, this.iface[i])) {
522                return i;
523            }
524        }
525        return -1;
526    }
527
528    /**
529     * Splice in {@link #operations} from the given {@link NetworkStats} based
530     * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
531     * since operation counts are at data layer.
532     */
533    public void spliceOperationsFrom(NetworkStats stats) {
534        for (int i = 0; i < size; i++) {
535            final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i],
536                    defaultNetwork[i]);
537            if (j == -1) {
538                operations[i] = 0;
539            } else {
540                operations[i] = stats.operations[j];
541            }
542        }
543    }
544
545    /**
546     * Return list of unique interfaces known by this data structure.
547     */
548    public String[] getUniqueIfaces() {
549        final HashSet<String> ifaces = new HashSet<String>();
550        for (String iface : this.iface) {
551            if (iface != IFACE_ALL) {
552                ifaces.add(iface);
553            }
554        }
555        return ifaces.toArray(new String[ifaces.size()]);
556    }
557
558    /**
559     * Return list of unique UIDs known by this data structure.
560     */
561    public int[] getUniqueUids() {
562        final SparseBooleanArray uids = new SparseBooleanArray();
563        for (int uid : this.uid) {
564            uids.put(uid, true);
565        }
566
567        final int size = uids.size();
568        final int[] result = new int[size];
569        for (int i = 0; i < size; i++) {
570            result[i] = uids.keyAt(i);
571        }
572        return result;
573    }
574
575    /**
576     * Return total bytes represented by this snapshot object, usually used when
577     * checking if a {@link #subtract(NetworkStats)} delta passes a threshold.
578     */
579    public long getTotalBytes() {
580        final Entry entry = getTotal(null);
581        return entry.rxBytes + entry.txBytes;
582    }
583
584    /**
585     * Return total of all fields represented by this snapshot object.
586     */
587    public Entry getTotal(Entry recycle) {
588        return getTotal(recycle, null, UID_ALL, false);
589    }
590
591    /**
592     * Return total of all fields represented by this snapshot object matching
593     * the requested {@link #uid}.
594     */
595    public Entry getTotal(Entry recycle, int limitUid) {
596        return getTotal(recycle, null, limitUid, false);
597    }
598
599    /**
600     * Return total of all fields represented by this snapshot object matching
601     * the requested {@link #iface}.
602     */
603    public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
604        return getTotal(recycle, limitIface, UID_ALL, false);
605    }
606
607    public Entry getTotalIncludingTags(Entry recycle) {
608        return getTotal(recycle, null, UID_ALL, true);
609    }
610
611    /**
612     * Return total of all fields represented by this snapshot object matching
613     * the requested {@link #iface} and {@link #uid}.
614     *
615     * @param limitIface Set of {@link #iface} to include in total; or {@code
616     *            null} to include all ifaces.
617     */
618    private Entry getTotal(
619            Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) {
620        final Entry entry = recycle != null ? recycle : new Entry();
621
622        entry.iface = IFACE_ALL;
623        entry.uid = limitUid;
624        entry.set = SET_ALL;
625        entry.tag = TAG_NONE;
626        entry.metered = METERED_ALL;
627        entry.roaming = ROAMING_ALL;
628        entry.defaultNetwork = DEFAULT_NETWORK_ALL;
629        entry.rxBytes = 0;
630        entry.rxPackets = 0;
631        entry.txBytes = 0;
632        entry.txPackets = 0;
633        entry.operations = 0;
634
635        for (int i = 0; i < size; i++) {
636            final boolean matchesUid = (limitUid == UID_ALL) || (limitUid == uid[i]);
637            final boolean matchesIface = (limitIface == null) || (limitIface.contains(iface[i]));
638
639            if (matchesUid && matchesIface) {
640                // skip specific tags, since already counted in TAG_NONE
641                if (tag[i] != TAG_NONE && !includeTags) continue;
642
643                entry.rxBytes += rxBytes[i];
644                entry.rxPackets += rxPackets[i];
645                entry.txBytes += txBytes[i];
646                entry.txPackets += txPackets[i];
647                entry.operations += operations[i];
648            }
649        }
650        return entry;
651    }
652
653    /**
654     * Fast path for battery stats.
655     */
656    public long getTotalPackets() {
657        long total = 0;
658        for (int i = size-1; i >= 0; i--) {
659            total += rxPackets[i] + txPackets[i];
660        }
661        return total;
662    }
663
664    /**
665     * Subtract the given {@link NetworkStats}, effectively leaving the delta
666     * between two snapshots in time. Assumes that statistics rows collect over
667     * time, and that none of them have disappeared.
668     */
669    public NetworkStats subtract(NetworkStats right) {
670        return subtract(this, right, null, null);
671    }
672
673    /**
674     * Subtract the two given {@link NetworkStats} objects, returning the delta
675     * between two snapshots in time. Assumes that statistics rows collect over
676     * time, and that none of them have disappeared.
677     * <p>
678     * If counters have rolled backwards, they are clamped to {@code 0} and
679     * reported to the given {@link NonMonotonicObserver}.
680     */
681    public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
682            NonMonotonicObserver<C> observer, C cookie) {
683        return subtract(left, right, observer, cookie, null);
684    }
685
686    /**
687     * Subtract the two given {@link NetworkStats} objects, returning the delta
688     * between two snapshots in time. Assumes that statistics rows collect over
689     * time, and that none of them have disappeared.
690     * <p>
691     * If counters have rolled backwards, they are clamped to {@code 0} and
692     * reported to the given {@link NonMonotonicObserver}.
693     * <p>
694     * If <var>recycle</var> is supplied, this NetworkStats object will be
695     * reused (and returned) as the result if it is large enough to contain
696     * the data.
697     */
698    public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
699            NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) {
700        long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
701        if (deltaRealtime < 0) {
702            if (observer != null) {
703                observer.foundNonMonotonic(left, -1, right, -1, cookie);
704            }
705            deltaRealtime = 0;
706        }
707
708        // result will have our rows, and elapsed time between snapshots
709        final Entry entry = new Entry();
710        final NetworkStats result;
711        if (recycle != null && recycle.capacity >= left.size) {
712            result = recycle;
713            result.size = 0;
714            result.elapsedRealtime = deltaRealtime;
715        } else {
716            result = new NetworkStats(deltaRealtime, left.size);
717        }
718        for (int i = 0; i < left.size; i++) {
719            entry.iface = left.iface[i];
720            entry.uid = left.uid[i];
721            entry.set = left.set[i];
722            entry.tag = left.tag[i];
723            entry.metered = left.metered[i];
724            entry.roaming = left.roaming[i];
725            entry.defaultNetwork = left.defaultNetwork[i];
726            entry.rxBytes = left.rxBytes[i];
727            entry.rxPackets = left.rxPackets[i];
728            entry.txBytes = left.txBytes[i];
729            entry.txPackets = left.txPackets[i];
730            entry.operations = left.operations[i];
731
732            // find remote row that matches, and subtract
733            final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
734                    entry.metered, entry.roaming, entry.defaultNetwork, i);
735            if (j != -1) {
736                // Found matching row, subtract remote value.
737                entry.rxBytes -= right.rxBytes[j];
738                entry.rxPackets -= right.rxPackets[j];
739                entry.txBytes -= right.txBytes[j];
740                entry.txPackets -= right.txPackets[j];
741                entry.operations -= right.operations[j];
742            }
743
744            if (entry.isNegative()) {
745                if (observer != null) {
746                    observer.foundNonMonotonic(left, i, right, j, cookie);
747                }
748                entry.rxBytes = Math.max(entry.rxBytes, 0);
749                entry.rxPackets = Math.max(entry.rxPackets, 0);
750                entry.txBytes = Math.max(entry.txBytes, 0);
751                entry.txPackets = Math.max(entry.txPackets, 0);
752                entry.operations = Math.max(entry.operations, 0);
753            }
754
755            result.addValues(entry);
756        }
757
758        return result;
759    }
760
761    /**
762     * Return total statistics grouped by {@link #iface}; doesn't mutate the
763     * original structure.
764     */
765    public NetworkStats groupedByIface() {
766        final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
767
768        final Entry entry = new Entry();
769        entry.uid = UID_ALL;
770        entry.set = SET_ALL;
771        entry.tag = TAG_NONE;
772        entry.metered = METERED_ALL;
773        entry.roaming = ROAMING_ALL;
774        entry.defaultNetwork = DEFAULT_NETWORK_ALL;
775        entry.operations = 0L;
776
777        for (int i = 0; i < size; i++) {
778            // skip specific tags, since already counted in TAG_NONE
779            if (tag[i] != TAG_NONE) continue;
780
781            entry.iface = iface[i];
782            entry.rxBytes = rxBytes[i];
783            entry.rxPackets = rxPackets[i];
784            entry.txBytes = txBytes[i];
785            entry.txPackets = txPackets[i];
786            stats.combineValues(entry);
787        }
788
789        return stats;
790    }
791
792    /**
793     * Return total statistics grouped by {@link #uid}; doesn't mutate the
794     * original structure.
795     */
796    public NetworkStats groupedByUid() {
797        final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
798
799        final Entry entry = new Entry();
800        entry.iface = IFACE_ALL;
801        entry.set = SET_ALL;
802        entry.tag = TAG_NONE;
803        entry.metered = METERED_ALL;
804        entry.roaming = ROAMING_ALL;
805        entry.defaultNetwork = DEFAULT_NETWORK_ALL;
806
807        for (int i = 0; i < size; i++) {
808            // skip specific tags, since already counted in TAG_NONE
809            if (tag[i] != TAG_NONE) continue;
810
811            entry.uid = uid[i];
812            entry.rxBytes = rxBytes[i];
813            entry.rxPackets = rxPackets[i];
814            entry.txBytes = txBytes[i];
815            entry.txPackets = txPackets[i];
816            entry.operations = operations[i];
817            stats.combineValues(entry);
818        }
819
820        return stats;
821    }
822
823    /**
824     * Return all rows except those attributed to the requested UID; doesn't
825     * mutate the original structure.
826     */
827    public NetworkStats withoutUids(int[] uids) {
828        final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
829
830        Entry entry = new Entry();
831        for (int i = 0; i < size; i++) {
832            entry = getValues(i, entry);
833            if (!ArrayUtils.contains(uids, entry.uid)) {
834                stats.addValues(entry);
835            }
836        }
837
838        return stats;
839    }
840
841    /**
842     * Only keep entries that match all specified filters.
843     *
844     * <p>This mutates the original structure in place. After this method is called,
845     * size is the number of matching entries, and capacity is the previous capacity.
846     * @param limitUid UID to filter for, or {@link #UID_ALL}.
847     * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
848     * @param limitTag Tag to filter for, or {@link #TAG_ALL}.
849     */
850    public void filter(int limitUid, String[] limitIfaces, int limitTag) {
851        if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
852            return;
853        }
854
855        Entry entry = new Entry();
856        int nextOutputEntry = 0;
857        for (int i = 0; i < size; i++) {
858            entry = getValues(i, entry);
859            final boolean matches =
860                    (limitUid == UID_ALL || limitUid == entry.uid)
861                    && (limitTag == TAG_ALL || limitTag == entry.tag)
862                    && (limitIfaces == INTERFACES_ALL
863                            || ArrayUtils.contains(limitIfaces, entry.iface));
864
865            if (matches) {
866                setValues(nextOutputEntry, entry);
867                nextOutputEntry++;
868            }
869        }
870
871        size = nextOutputEntry;
872    }
873
874    public void dump(String prefix, PrintWriter pw) {
875        pw.print(prefix);
876        pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
877        for (int i = 0; i < size; i++) {
878            pw.print(prefix);
879            pw.print("  ["); pw.print(i); pw.print("]");
880            pw.print(" iface="); pw.print(iface[i]);
881            pw.print(" uid="); pw.print(uid[i]);
882            pw.print(" set="); pw.print(setToString(set[i]));
883            pw.print(" tag="); pw.print(tagToString(tag[i]));
884            pw.print(" metered="); pw.print(meteredToString(metered[i]));
885            pw.print(" roaming="); pw.print(roamingToString(roaming[i]));
886            pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i]));
887            pw.print(" rxBytes="); pw.print(rxBytes[i]);
888            pw.print(" rxPackets="); pw.print(rxPackets[i]);
889            pw.print(" txBytes="); pw.print(txBytes[i]);
890            pw.print(" txPackets="); pw.print(txPackets[i]);
891            pw.print(" operations="); pw.println(operations[i]);
892        }
893    }
894
895    /**
896     * Return text description of {@link #set} value.
897     */
898    public static String setToString(int set) {
899        switch (set) {
900            case SET_ALL:
901                return "ALL";
902            case SET_DEFAULT:
903                return "DEFAULT";
904            case SET_FOREGROUND:
905                return "FOREGROUND";
906            case SET_DBG_VPN_IN:
907                return "DBG_VPN_IN";
908            case SET_DBG_VPN_OUT:
909                return "DBG_VPN_OUT";
910            default:
911                return "UNKNOWN";
912        }
913    }
914
915    /**
916     * Return text description of {@link #set} value.
917     */
918    public static String setToCheckinString(int set) {
919        switch (set) {
920            case SET_ALL:
921                return "all";
922            case SET_DEFAULT:
923                return "def";
924            case SET_FOREGROUND:
925                return "fg";
926            case SET_DBG_VPN_IN:
927                return "vpnin";
928            case SET_DBG_VPN_OUT:
929                return "vpnout";
930            default:
931                return "unk";
932        }
933    }
934
935    /**
936     * @return true if the querySet matches the dataSet.
937     */
938    public static boolean setMatches(int querySet, int dataSet) {
939        if (querySet == dataSet) {
940            return true;
941        }
942        // SET_ALL matches all non-debugging sets.
943        return querySet == SET_ALL && dataSet < SET_DEBUG_START;
944    }
945
946    /**
947     * Return text description of {@link #tag} value.
948     */
949    public static String tagToString(int tag) {
950        return "0x" + Integer.toHexString(tag);
951    }
952
953    /**
954     * Return text description of {@link #metered} value.
955     */
956    public static String meteredToString(int metered) {
957        switch (metered) {
958            case METERED_ALL:
959                return "ALL";
960            case METERED_NO:
961                return "NO";
962            case METERED_YES:
963                return "YES";
964            default:
965                return "UNKNOWN";
966        }
967    }
968
969    /**
970     * Return text description of {@link #roaming} value.
971     */
972    public static String roamingToString(int roaming) {
973        switch (roaming) {
974            case ROAMING_ALL:
975                return "ALL";
976            case ROAMING_NO:
977                return "NO";
978            case ROAMING_YES:
979                return "YES";
980            default:
981                return "UNKNOWN";
982        }
983    }
984
985    /**
986     * Return text description of {@link #defaultNetwork} value.
987     */
988    public static String defaultNetworkToString(int defaultNetwork) {
989        switch (defaultNetwork) {
990            case DEFAULT_NETWORK_ALL:
991                return "ALL";
992            case DEFAULT_NETWORK_NO:
993                return "NO";
994            case DEFAULT_NETWORK_YES:
995                return "YES";
996            default:
997                return "UNKNOWN";
998        }
999    }
1000
1001    @Override
1002    public String toString() {
1003        final CharArrayWriter writer = new CharArrayWriter();
1004        dump("", new PrintWriter(writer));
1005        return writer.toString();
1006    }
1007
1008    @Override
1009    public int describeContents() {
1010        return 0;
1011    }
1012
1013    public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
1014        @Override
1015        public NetworkStats createFromParcel(Parcel in) {
1016            return new NetworkStats(in);
1017        }
1018
1019        @Override
1020        public NetworkStats[] newArray(int size) {
1021            return new NetworkStats[size];
1022        }
1023    };
1024
1025    public interface NonMonotonicObserver<C> {
1026        public void foundNonMonotonic(
1027                NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
1028    }
1029
1030    /**
1031     * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
1032     *
1033     * This method should only be called on delta NetworkStats. Do not call this method on a
1034     * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may
1035     * change over time.
1036     *
1037     * This method performs adjustments for one active VPN package and one VPN iface at a time.
1038     *
1039     * It is possible for the VPN software to use multiple underlying networks. This method
1040     * only migrates traffic for the primary underlying network.
1041     *
1042     * @param tunUid uid of the VPN application
1043     * @param tunIface iface of the vpn tunnel
1044     * @param underlyingIface the primary underlying network iface used by the VPN application
1045     * @return true if it successfully adjusts the accounting for VPN, false otherwise
1046     */
1047    public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) {
1048        Entry tunIfaceTotal = new Entry();
1049        Entry underlyingIfaceTotal = new Entry();
1050
1051        tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal);
1052
1053        // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app.
1054        // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression.
1055        // Negative stats should be avoided.
1056        Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal);
1057        if (pool.isEmpty()) {
1058            return true;
1059        }
1060        Entry moved =
1061                addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
1062        deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
1063
1064        if (!moved.isEmpty()) {
1065            Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved="
1066                    + moved);
1067            return false;
1068        }
1069        return true;
1070    }
1071
1072    /**
1073     * Initializes the data used by the migrateTun() method.
1074     *
1075     * This is the first pass iteration which does the following work:
1076     * (1) Adds up all the traffic through the tunUid's underlyingIface
1077     *     (both foreground and background).
1078     * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
1079     */
1080    private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface,
1081            Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
1082        Entry recycle = new Entry();
1083        for (int i = 0; i < size; i++) {
1084            getValues(i, recycle);
1085            if (recycle.uid == UID_ALL) {
1086                throw new IllegalStateException(
1087                        "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
1088            } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
1089                throw new IllegalStateException(
1090                        "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
1091            }
1092
1093            if (recycle.uid == tunUid && recycle.tag == TAG_NONE
1094                    && Objects.equals(underlyingIface, recycle.iface)) {
1095                underlyingIfaceTotal.add(recycle);
1096            }
1097
1098            if (recycle.uid != tunUid && recycle.tag == TAG_NONE
1099                    && Objects.equals(tunIface, recycle.iface)) {
1100                // Add up all tunIface traffic excluding traffic from the vpn app itself.
1101                tunIfaceTotal.add(recycle);
1102            }
1103        }
1104    }
1105
1106    private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
1107        Entry pool = new Entry();
1108        pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes);
1109        pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets);
1110        pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes);
1111        pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets);
1112        pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations);
1113        return pool;
1114    }
1115
1116    private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface,
1117            Entry tunIfaceTotal, Entry pool) {
1118        Entry moved = new Entry();
1119        Entry tmpEntry = new Entry();
1120        tmpEntry.iface = underlyingIface;
1121        for (int i = 0; i < size; i++) {
1122            // the vpn app is excluded from the redistribution but all moved traffic will be
1123            // deducted from the vpn app (see deductTrafficFromVpnApp below).
1124            if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) {
1125                if (tunIfaceTotal.rxBytes > 0) {
1126                    tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
1127                } else {
1128                    tmpEntry.rxBytes = 0;
1129                }
1130                if (tunIfaceTotal.rxPackets > 0) {
1131                    tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
1132                } else {
1133                    tmpEntry.rxPackets = 0;
1134                }
1135                if (tunIfaceTotal.txBytes > 0) {
1136                    tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
1137                } else {
1138                    tmpEntry.txBytes = 0;
1139                }
1140                if (tunIfaceTotal.txPackets > 0) {
1141                    tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
1142                } else {
1143                    tmpEntry.txPackets = 0;
1144                }
1145                if (tunIfaceTotal.operations > 0) {
1146                    tmpEntry.operations =
1147                            pool.operations * operations[i] / tunIfaceTotal.operations;
1148                } else {
1149                    tmpEntry.operations = 0;
1150                }
1151                tmpEntry.uid = uid[i];
1152                tmpEntry.tag = tag[i];
1153                tmpEntry.set = set[i];
1154                tmpEntry.metered = metered[i];
1155                tmpEntry.roaming = roaming[i];
1156                tmpEntry.defaultNetwork = defaultNetwork[i];
1157                combineValues(tmpEntry);
1158                if (tag[i] == TAG_NONE) {
1159                    moved.add(tmpEntry);
1160                    // Add debug info
1161                    tmpEntry.set = SET_DBG_VPN_IN;
1162                    combineValues(tmpEntry);
1163                }
1164            }
1165        }
1166        return moved;
1167    }
1168
1169    private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
1170        // Add debug info
1171        moved.uid = tunUid;
1172        moved.set = SET_DBG_VPN_OUT;
1173        moved.tag = TAG_NONE;
1174        moved.iface = underlyingIface;
1175        moved.metered = METERED_ALL;
1176        moved.roaming = ROAMING_ALL;
1177        moved.defaultNetwork = DEFAULT_NETWORK_ALL;
1178        combineValues(moved);
1179
1180        // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
1181        // the TAG_NONE traffic.
1182        //
1183        // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO,
1184        // which should be the case as it comes directly from the /proc file. We only blend in the
1185        // roaming data after applying these adjustments, by checking the NetworkIdentity of the
1186        // underlying iface.
1187        int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
1188                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
1189        if (idxVpnBackground != -1) {
1190            tunSubtract(idxVpnBackground, this, moved);
1191        }
1192
1193        int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
1194                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
1195        if (idxVpnForeground != -1) {
1196            tunSubtract(idxVpnForeground, this, moved);
1197        }
1198    }
1199
1200    private static void tunSubtract(int i, NetworkStats left, Entry right) {
1201        long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
1202        left.rxBytes[i] -= rxBytes;
1203        right.rxBytes -= rxBytes;
1204
1205        long rxPackets = Math.min(left.rxPackets[i], right.rxPackets);
1206        left.rxPackets[i] -= rxPackets;
1207        right.rxPackets -= rxPackets;
1208
1209        long txBytes = Math.min(left.txBytes[i], right.txBytes);
1210        left.txBytes[i] -= txBytes;
1211        right.txBytes -= txBytes;
1212
1213        long txPackets = Math.min(left.txPackets[i], right.txPackets);
1214        left.txPackets[i] -= txPackets;
1215        right.txPackets -= txPackets;
1216    }
1217}
1218