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