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