• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.os.Build;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.Process;
28 import android.os.SystemClock;
29 import android.util.SparseBooleanArray;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.internal.util.ArrayUtils;
33 
34 import libcore.util.EmptyArray;
35 
36 import java.io.CharArrayWriter;
37 import java.io.PrintWriter;
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.util.Arrays;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45 import java.util.function.Predicate;
46 
47 /**
48  * Collection of active network statistics. Can contain summary details across
49  * all interfaces, or details with per-UID granularity. Internally stores data
50  * as a large table, closely matching {@code /proc/} data format. This structure
51  * optimizes for rapid in-memory comparison, but consider using
52  * {@link NetworkStatsHistory} when persisting.
53  *
54  * @hide
55  */
56 // @NotThreadSafe
57 @SystemApi
58 public final class NetworkStats implements Parcelable {
59     private static final String TAG = "NetworkStats";
60 
61     /**
62      * {@link #iface} value when interface details unavailable.
63      * @hide
64      */
65     @Nullable public static final String IFACE_ALL = null;
66 
67     /**
68      * Virtual network interface for video telephony. This is for VT data usage counting
69      * purpose.
70      */
71     public static final String IFACE_VT = "vt_data0";
72 
73     /** {@link #uid} value when UID details unavailable. */
74     public static final int UID_ALL = -1;
75     /** Special UID value for data usage by tethering. */
76     public static final int UID_TETHERING = -5;
77 
78     /**
79      * {@link #tag} value matching any tag.
80      * @hide
81      */
82     // TODO: Rename TAG_ALL to TAG_ANY.
83     public static final int TAG_ALL = -1;
84     /**
85      * {@link #set} value for all sets combined, not including debug sets.
86      * @hide
87      */
88     public static final int SET_ALL = -1;
89     /** {@link #set} value where background data is accounted. */
90     public static final int SET_DEFAULT = 0;
91     /** {@link #set} value where foreground data is accounted. */
92     public static final int SET_FOREGROUND = 1;
93     /**
94      * All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values.
95      * @hide
96      */
97     public static final int SET_DEBUG_START = 1000;
98     /**
99      * Debug {@link #set} value when the VPN stats are moved in.
100      * @hide
101      */
102     public static final int SET_DBG_VPN_IN = 1001;
103     /**
104      * Debug {@link #set} value when the VPN stats are moved out of a vpn UID.
105      * @hide
106      */
107     public static final int SET_DBG_VPN_OUT = 1002;
108 
109     /** @hide */
110     @Retention(RetentionPolicy.SOURCE)
111     @IntDef(prefix = { "SET_" }, value = {
112             SET_ALL,
113             SET_DEFAULT,
114             SET_FOREGROUND,
115             SET_DEBUG_START,
116             SET_DBG_VPN_IN,
117             SET_DBG_VPN_OUT
118     })
119     public @interface State {
120     }
121 
122     /**
123      * Include all interfaces when filtering
124      * @hide
125      */
126     public @Nullable static final String[] INTERFACES_ALL = null;
127 
128     /** {@link #tag} value for total data across all tags. */
129     // TODO: Rename TAG_NONE to TAG_ALL.
130     public static final int TAG_NONE = 0;
131 
132     /**
133      * {@link #metered} value to account for all metered states.
134      * @hide
135      */
136     public static final int METERED_ALL = -1;
137     /** {@link #metered} value where native, unmetered data is accounted. */
138     public static final int METERED_NO = 0;
139     /** {@link #metered} value where metered data is accounted. */
140     public static final int METERED_YES = 1;
141 
142     /** @hide */
143     @Retention(RetentionPolicy.SOURCE)
144     @IntDef(prefix = { "METERED_" }, value = {
145             METERED_ALL,
146             METERED_NO,
147             METERED_YES
148     })
149     public @interface Meteredness {
150     }
151 
152 
153     /**
154      * {@link #roaming} value to account for all roaming states.
155      * @hide
156      */
157     public static final int ROAMING_ALL = -1;
158     /** {@link #roaming} value where native, non-roaming data is accounted. */
159     public static final int ROAMING_NO = 0;
160     /** {@link #roaming} value where roaming data is accounted. */
161     public static final int ROAMING_YES = 1;
162 
163     /** @hide */
164     @Retention(RetentionPolicy.SOURCE)
165     @IntDef(prefix = { "ROAMING_" }, value = {
166             ROAMING_ALL,
167             ROAMING_NO,
168             ROAMING_YES
169     })
170     public @interface Roaming {
171     }
172 
173     /**
174      * {@link #onDefaultNetwork} value to account for all default network states.
175      * @hide
176      */
177     public static final int DEFAULT_NETWORK_ALL = -1;
178     /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
179     public static final int DEFAULT_NETWORK_NO = 0;
180     /** {@link #onDefaultNetwork} value to account for usage while the default network. */
181     public static final int DEFAULT_NETWORK_YES = 1;
182 
183     /** @hide */
184     @Retention(RetentionPolicy.SOURCE)
185     @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
186             DEFAULT_NETWORK_ALL,
187             DEFAULT_NETWORK_NO,
188             DEFAULT_NETWORK_YES
189     })
190     public @interface DefaultNetwork {
191     }
192 
193     /**
194      * Denotes a request for stats at the interface level.
195      * @hide
196      */
197     public static final int STATS_PER_IFACE = 0;
198     /**
199      * Denotes a request for stats at the interface and UID level.
200      * @hide
201      */
202     public static final int STATS_PER_UID = 1;
203 
204     /** @hide */
205     @Retention(RetentionPolicy.SOURCE)
206     @IntDef(prefix = { "STATS_PER_" }, value = {
207             STATS_PER_IFACE,
208             STATS_PER_UID
209     })
210     public @interface StatsType {
211     }
212 
213     private static final String CLATD_INTERFACE_PREFIX = "v4-";
214     // Delta between IPv4 header (20b) and IPv6 header (40b).
215     // Used for correct stats accounting on clatd interfaces.
216     private static final int IPV4V6_HEADER_DELTA = 20;
217 
218     // TODO: move fields to "mVariable" notation
219 
220     /**
221      * {@link SystemClock#elapsedRealtime()} timestamp when this data was
222      * generated.
223      */
224     private long elapsedRealtime;
225     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
226     private int size;
227     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
228     private int capacity;
229     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
230     private String[] iface;
231     @UnsupportedAppUsage
232     private int[] uid;
233     @UnsupportedAppUsage
234     private int[] set;
235     @UnsupportedAppUsage
236     private int[] tag;
237     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
238     private int[] metered;
239     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
240     private int[] roaming;
241     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
242     private int[] defaultNetwork;
243     @UnsupportedAppUsage
244     private long[] rxBytes;
245     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
246     private long[] rxPackets;
247     @UnsupportedAppUsage
248     private long[] txBytes;
249     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
250     private long[] txPackets;
251     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
252     private long[] operations;
253 
254     /**
255      * Basic element of network statistics. Contains the number of packets and number of bytes
256      * transferred on both directions in a given set of conditions. See
257      * {@link Entry#Entry(String, int, int, int, int, int, int, long, long, long, long, long)}.
258      *
259      * @hide
260      */
261     @SystemApi
262     public static class Entry {
263         /** @hide */
264         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
265         public String iface;
266         /** @hide */
267         @UnsupportedAppUsage
268         public int uid;
269         /** @hide */
270         @UnsupportedAppUsage
271         public int set;
272         /** @hide */
273         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
274         public int tag;
275         /**
276          * Note that this is only populated w/ the default value when read from /proc or written
277          * to disk. We merge in the correct value when reporting this value to clients of
278          * getSummary().
279          * @hide
280          */
281         public int metered;
282         /**
283          * Note that this is only populated w/ the default value when read from /proc or written
284          * to disk. We merge in the correct value when reporting this value to clients of
285          * getSummary().
286          * @hide
287          */
288         public int roaming;
289         /**
290          * Note that this is only populated w/ the default value when read from /proc or written
291          * to disk. We merge in the correct value when reporting this value to clients of
292          * getSummary().
293          * @hide
294          */
295         public int defaultNetwork;
296         /** @hide */
297         @UnsupportedAppUsage
298         public long rxBytes;
299         /** @hide */
300         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
301         public long rxPackets;
302         /** @hide */
303         @UnsupportedAppUsage
304         public long txBytes;
305         /** @hide */
306         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
307         public long txPackets;
308         /** @hide */
309         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
310         public long operations;
311 
312         /** @hide */
313         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Entry()314         public Entry() {
315             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
316         }
317 
318         /** @hide */
Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)319         public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
320             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
321                     operations);
322         }
323 
324         /** @hide */
Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)325         public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
326                 long txBytes, long txPackets, long operations) {
327             this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
328                     rxBytes, rxPackets, txBytes, txPackets, operations);
329         }
330 
331         /**
332          * Construct a {@link Entry} object by giving statistics of packet and byte transferred on
333          * both direction, and associated with a set of given conditions.
334          *
335          * @param iface interface name of this {@link Entry}. Or null if not specified.
336          * @param uid uid of this {@link Entry}. {@link #UID_TETHERING} if this {@link Entry} is
337          *            for tethering. Or {@link #UID_ALL} if this {@link NetworkStats} is only
338          *            counting iface stats.
339          * @param set usage state of this {@link Entry}. Should be one of the following
340          *            values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
341          * @param tag tag of this {@link Entry}.
342          * @param metered metered state of this {@link Entry}. Should be one of the following
343          *                values: {link #METERED_YES}, {link #METERED_NO}.
344          * @param roaming roaming state of this {@link Entry}. Should be one of the following
345          *                values: {link #ROAMING_YES}, {link #ROAMING_NO}.
346          * @param defaultNetwork default network status of this {@link Entry}. Should be one
347          *                       of the following values: {link #DEFAULT_NETWORK_YES},
348          *                       {link #DEFAULT_NETWORK_NO}.
349          * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
350          *                represent the contents of IP packets, including IP headers.
351          * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
352          *                  represent the contents of IP packets, including IP headers.
353          * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
354          *                represent the contents of IP packets, including IP headers.
355          * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
356          *                  represent the contents of IP packets, including IP headers.
357          * @param operations count of network operations performed for this {@link Entry}. This can
358          *                   be used to derive bytes-per-operation.
359          */
Entry(@ullable String iface, int uid, @State int set, int tag, @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)360         public Entry(@Nullable String iface, int uid, @State int set, int tag,
361                 @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
362                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
363             this.iface = iface;
364             this.uid = uid;
365             this.set = set;
366             this.tag = tag;
367             this.metered = metered;
368             this.roaming = roaming;
369             this.defaultNetwork = defaultNetwork;
370             this.rxBytes = rxBytes;
371             this.rxPackets = rxPackets;
372             this.txBytes = txBytes;
373             this.txPackets = txPackets;
374             this.operations = operations;
375         }
376 
377         /** @hide */
isNegative()378         public boolean isNegative() {
379             return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
380         }
381 
382         /** @hide */
isEmpty()383         public boolean isEmpty() {
384             return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
385                     && operations == 0;
386         }
387 
388         /** @hide */
add(Entry another)389         public void add(Entry another) {
390             this.rxBytes += another.rxBytes;
391             this.rxPackets += another.rxPackets;
392             this.txBytes += another.txBytes;
393             this.txPackets += another.txPackets;
394             this.operations += another.operations;
395         }
396 
397         @Override
toString()398         public String toString() {
399             final StringBuilder builder = new StringBuilder();
400             builder.append("iface=").append(iface);
401             builder.append(" uid=").append(uid);
402             builder.append(" set=").append(setToString(set));
403             builder.append(" tag=").append(tagToString(tag));
404             builder.append(" metered=").append(meteredToString(metered));
405             builder.append(" roaming=").append(roamingToString(roaming));
406             builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork));
407             builder.append(" rxBytes=").append(rxBytes);
408             builder.append(" rxPackets=").append(rxPackets);
409             builder.append(" txBytes=").append(txBytes);
410             builder.append(" txPackets=").append(txPackets);
411             builder.append(" operations=").append(operations);
412             return builder.toString();
413         }
414 
415         /** @hide */
416         @Override
equals(@ullable Object o)417         public boolean equals(@Nullable Object o) {
418             if (o instanceof Entry) {
419                 final Entry e = (Entry) o;
420                 return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
421                         && roaming == e.roaming && defaultNetwork == e.defaultNetwork
422                         && rxBytes == e.rxBytes && rxPackets == e.rxPackets
423                         && txBytes == e.txBytes && txPackets == e.txPackets
424                         && operations == e.operations && iface.equals(e.iface);
425             }
426             return false;
427         }
428 
429         /** @hide */
430         @Override
hashCode()431         public int hashCode() {
432             return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface);
433         }
434     }
435 
NetworkStats(long elapsedRealtime, int initialSize)436     public NetworkStats(long elapsedRealtime, int initialSize) {
437         this.elapsedRealtime = elapsedRealtime;
438         this.size = 0;
439         if (initialSize > 0) {
440             this.capacity = initialSize;
441             this.iface = new String[initialSize];
442             this.uid = new int[initialSize];
443             this.set = new int[initialSize];
444             this.tag = new int[initialSize];
445             this.metered = new int[initialSize];
446             this.roaming = new int[initialSize];
447             this.defaultNetwork = new int[initialSize];
448             this.rxBytes = new long[initialSize];
449             this.rxPackets = new long[initialSize];
450             this.txBytes = new long[initialSize];
451             this.txPackets = new long[initialSize];
452             this.operations = new long[initialSize];
453         } else {
454             // Special case for use by NetworkStatsFactory to start out *really* empty.
455             clear();
456         }
457     }
458 
459     /** @hide */
460     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
NetworkStats(Parcel parcel)461     public NetworkStats(Parcel parcel) {
462         elapsedRealtime = parcel.readLong();
463         size = parcel.readInt();
464         capacity = parcel.readInt();
465         iface = parcel.createStringArray();
466         uid = parcel.createIntArray();
467         set = parcel.createIntArray();
468         tag = parcel.createIntArray();
469         metered = parcel.createIntArray();
470         roaming = parcel.createIntArray();
471         defaultNetwork = parcel.createIntArray();
472         rxBytes = parcel.createLongArray();
473         rxPackets = parcel.createLongArray();
474         txBytes = parcel.createLongArray();
475         txPackets = parcel.createLongArray();
476         operations = parcel.createLongArray();
477     }
478 
479     @Override
writeToParcel(@onNull Parcel dest, int flags)480     public void writeToParcel(@NonNull Parcel dest, int flags) {
481         dest.writeLong(elapsedRealtime);
482         dest.writeInt(size);
483         dest.writeInt(capacity);
484         dest.writeStringArray(iface);
485         dest.writeIntArray(uid);
486         dest.writeIntArray(set);
487         dest.writeIntArray(tag);
488         dest.writeIntArray(metered);
489         dest.writeIntArray(roaming);
490         dest.writeIntArray(defaultNetwork);
491         dest.writeLongArray(rxBytes);
492         dest.writeLongArray(rxPackets);
493         dest.writeLongArray(txBytes);
494         dest.writeLongArray(txPackets);
495         dest.writeLongArray(operations);
496     }
497 
498     /**
499      * @hide
500      */
501     @Override
clone()502     public NetworkStats clone() {
503         final NetworkStats clone = new NetworkStats(elapsedRealtime, size);
504         NetworkStats.Entry entry = null;
505         for (int i = 0; i < size; i++) {
506             entry = getValues(i, entry);
507             clone.insertEntry(entry);
508         }
509         return clone;
510     }
511 
512     /**
513      * Clear all data stored in this object.
514      * @hide
515      */
clear()516     public void clear() {
517         this.capacity = 0;
518         this.iface = EmptyArray.STRING;
519         this.uid = EmptyArray.INT;
520         this.set = EmptyArray.INT;
521         this.tag = EmptyArray.INT;
522         this.metered = EmptyArray.INT;
523         this.roaming = EmptyArray.INT;
524         this.defaultNetwork = EmptyArray.INT;
525         this.rxBytes = EmptyArray.LONG;
526         this.rxPackets = EmptyArray.LONG;
527         this.txBytes = EmptyArray.LONG;
528         this.txPackets = EmptyArray.LONG;
529         this.operations = EmptyArray.LONG;
530     }
531 
532     /** @hide */
533     @VisibleForTesting
insertEntry( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets)534     public NetworkStats insertEntry(
535             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
536         return insertEntry(
537                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
538     }
539 
540     /** @hide */
541     @VisibleForTesting
insertEntry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)542     public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes,
543             long rxPackets, long txBytes, long txPackets, long operations) {
544         return insertEntry(new Entry(
545                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
546     }
547 
548     /** @hide */
549     @VisibleForTesting
insertEntry(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)550     public NetworkStats insertEntry(String iface, int uid, int set, int tag, int metered,
551             int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
552             long txPackets, long operations) {
553         return insertEntry(new Entry(
554                 iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
555                 txBytes, txPackets, operations));
556     }
557 
558     /**
559      * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
560      * object can be recycled across multiple calls.
561      * @hide
562      */
insertEntry(Entry entry)563     public NetworkStats insertEntry(Entry entry) {
564         if (size >= capacity) {
565             final int newLength = Math.max(size, 10) * 3 / 2;
566             iface = Arrays.copyOf(iface, newLength);
567             uid = Arrays.copyOf(uid, newLength);
568             set = Arrays.copyOf(set, newLength);
569             tag = Arrays.copyOf(tag, newLength);
570             metered = Arrays.copyOf(metered, newLength);
571             roaming = Arrays.copyOf(roaming, newLength);
572             defaultNetwork = Arrays.copyOf(defaultNetwork, newLength);
573             rxBytes = Arrays.copyOf(rxBytes, newLength);
574             rxPackets = Arrays.copyOf(rxPackets, newLength);
575             txBytes = Arrays.copyOf(txBytes, newLength);
576             txPackets = Arrays.copyOf(txPackets, newLength);
577             operations = Arrays.copyOf(operations, newLength);
578             capacity = newLength;
579         }
580 
581         setValues(size, entry);
582         size++;
583 
584         return this;
585     }
586 
setValues(int i, Entry entry)587     private void setValues(int i, Entry entry) {
588         iface[i] = entry.iface;
589         uid[i] = entry.uid;
590         set[i] = entry.set;
591         tag[i] = entry.tag;
592         metered[i] = entry.metered;
593         roaming[i] = entry.roaming;
594         defaultNetwork[i] = entry.defaultNetwork;
595         rxBytes[i] = entry.rxBytes;
596         rxPackets[i] = entry.rxPackets;
597         txBytes[i] = entry.txBytes;
598         txPackets[i] = entry.txPackets;
599         operations[i] = entry.operations;
600     }
601 
602     /**
603      * Return specific stats entry.
604      * @hide
605      */
606     @UnsupportedAppUsage
getValues(int i, Entry recycle)607     public Entry getValues(int i, Entry recycle) {
608         final Entry entry = recycle != null ? recycle : new Entry();
609         entry.iface = iface[i];
610         entry.uid = uid[i];
611         entry.set = set[i];
612         entry.tag = tag[i];
613         entry.metered = metered[i];
614         entry.roaming = roaming[i];
615         entry.defaultNetwork = defaultNetwork[i];
616         entry.rxBytes = rxBytes[i];
617         entry.rxPackets = rxPackets[i];
618         entry.txBytes = txBytes[i];
619         entry.txPackets = txPackets[i];
620         entry.operations = operations[i];
621         return entry;
622     }
623 
624     /**
625      * If @{code dest} is not equal to @{code src}, copy entry from index @{code src} to index
626      * @{code dest}.
627      */
maybeCopyEntry(int dest, int src)628     private void maybeCopyEntry(int dest, int src) {
629         if (dest == src) return;
630         iface[dest] = iface[src];
631         uid[dest] = uid[src];
632         set[dest] = set[src];
633         tag[dest] = tag[src];
634         metered[dest] = metered[src];
635         roaming[dest] = roaming[src];
636         defaultNetwork[dest] = defaultNetwork[src];
637         rxBytes[dest] = rxBytes[src];
638         rxPackets[dest] = rxPackets[src];
639         txBytes[dest] = txBytes[src];
640         txPackets[dest] = txPackets[src];
641         operations[dest] = operations[src];
642     }
643 
644     /** @hide */
getElapsedRealtime()645     public long getElapsedRealtime() {
646         return elapsedRealtime;
647     }
648 
649     /** @hide */
setElapsedRealtime(long time)650     public void setElapsedRealtime(long time) {
651         elapsedRealtime = time;
652     }
653 
654     /**
655      * Return age of this {@link NetworkStats} object with respect to
656      * {@link SystemClock#elapsedRealtime()}.
657      * @hide
658      */
getElapsedRealtimeAge()659     public long getElapsedRealtimeAge() {
660         return SystemClock.elapsedRealtime() - elapsedRealtime;
661     }
662 
663     /** @hide */
664     @UnsupportedAppUsage
size()665     public int size() {
666         return size;
667     }
668 
669     /** @hide */
670     @VisibleForTesting
internalSize()671     public int internalSize() {
672         return capacity;
673     }
674 
675     /** @hide */
676     @Deprecated
combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)677     public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
678             long txBytes, long txPackets, long operations) {
679         return combineValues(
680                 iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes,
681                 txPackets, operations);
682     }
683 
684     /** @hide */
combineValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)685     public NetworkStats combineValues(String iface, int uid, int set, int tag,
686             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
687         return combineValues(new Entry(
688                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
689     }
690 
691     /**
692      * Combine given values with an existing row, or create a new row if
693      * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can
694      * also be used to subtract values from existing rows. This method mutates the referencing
695      * {@link NetworkStats} object.
696      *
697      * @param entry the {@link Entry} to combine.
698      * @return a reference to this mutated {@link NetworkStats} object.
699      * @hide
700      */
combineValues(@onNull Entry entry)701     public @NonNull NetworkStats combineValues(@NonNull Entry entry) {
702         final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered,
703                 entry.roaming, entry.defaultNetwork);
704         if (i == -1) {
705             // only create new entry when positive contribution
706             insertEntry(entry);
707         } else {
708             rxBytes[i] += entry.rxBytes;
709             rxPackets[i] += entry.rxPackets;
710             txBytes[i] += entry.txBytes;
711             txPackets[i] += entry.txPackets;
712             operations[i] += entry.operations;
713         }
714         return this;
715     }
716 
717     /**
718      * Add given values with an existing row, or create a new row if
719      * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can
720      * also be used to subtract values from existing rows.
721      *
722      * @param entry the {@link Entry} to add.
723      * @return a new constructed {@link NetworkStats} object that contains the result.
724      */
addEntry(@onNull Entry entry)725     public @NonNull NetworkStats addEntry(@NonNull Entry entry) {
726         return this.clone().combineValues(entry);
727     }
728 
729     /**
730      * Add the given {@link NetworkStats} objects.
731      *
732      * @return the sum of two objects.
733      */
add(@onNull NetworkStats another)734     public @NonNull NetworkStats add(@NonNull NetworkStats another) {
735         final NetworkStats ret = this.clone();
736         ret.combineAllValues(another);
737         return ret;
738     }
739 
740     /**
741      * Combine all values from another {@link NetworkStats} into this object.
742      * @hide
743      */
combineAllValues(@onNull NetworkStats another)744     public void combineAllValues(@NonNull NetworkStats another) {
745         NetworkStats.Entry entry = null;
746         for (int i = 0; i < another.size; i++) {
747             entry = another.getValues(i, entry);
748             combineValues(entry);
749         }
750     }
751 
752     /**
753      * Find first stats index that matches the requested parameters.
754      * @hide
755      */
findIndex(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork)756     public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming,
757             int defaultNetwork) {
758         for (int i = 0; i < size; i++) {
759             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
760                     && metered == this.metered[i] && roaming == this.roaming[i]
761                     && defaultNetwork == this.defaultNetwork[i]
762                     && Objects.equals(iface, this.iface[i])) {
763                 return i;
764             }
765         }
766         return -1;
767     }
768 
769     /**
770      * Find first stats index that matches the requested parameters, starting
771      * search around the hinted index as an optimization.
772      * @hide
773      */
774     @VisibleForTesting
findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, int hintIndex)775     public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming,
776             int defaultNetwork, int hintIndex) {
777         for (int offset = 0; offset < size; offset++) {
778             final int halfOffset = offset / 2;
779 
780             // search outwards from hint index, alternating forward and backward
781             final int i;
782             if (offset % 2 == 0) {
783                 i = (hintIndex + halfOffset) % size;
784             } else {
785                 i = (size + hintIndex - halfOffset - 1) % size;
786             }
787 
788             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
789                     && metered == this.metered[i] && roaming == this.roaming[i]
790                     && defaultNetwork == this.defaultNetwork[i]
791                     && Objects.equals(iface, this.iface[i])) {
792                 return i;
793             }
794         }
795         return -1;
796     }
797 
798     /**
799      * Splice in {@link #operations} from the given {@link NetworkStats} based
800      * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
801      * since operation counts are at data layer.
802      * @hide
803      */
spliceOperationsFrom(NetworkStats stats)804     public void spliceOperationsFrom(NetworkStats stats) {
805         for (int i = 0; i < size; i++) {
806             final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i],
807                     defaultNetwork[i]);
808             if (j == -1) {
809                 operations[i] = 0;
810             } else {
811                 operations[i] = stats.operations[j];
812             }
813         }
814     }
815 
816     /**
817      * Return list of unique interfaces known by this data structure.
818      * @hide
819      */
getUniqueIfaces()820     public String[] getUniqueIfaces() {
821         final HashSet<String> ifaces = new HashSet<String>();
822         for (String iface : this.iface) {
823             if (iface != IFACE_ALL) {
824                 ifaces.add(iface);
825             }
826         }
827         return ifaces.toArray(new String[ifaces.size()]);
828     }
829 
830     /**
831      * Return list of unique UIDs known by this data structure.
832      * @hide
833      */
834     @UnsupportedAppUsage
getUniqueUids()835     public int[] getUniqueUids() {
836         final SparseBooleanArray uids = new SparseBooleanArray();
837         for (int uid : this.uid) {
838             uids.put(uid, true);
839         }
840 
841         final int size = uids.size();
842         final int[] result = new int[size];
843         for (int i = 0; i < size; i++) {
844             result[i] = uids.keyAt(i);
845         }
846         return result;
847     }
848 
849     /**
850      * Return total bytes represented by this snapshot object, usually used when
851      * checking if a {@link #subtract(NetworkStats)} delta passes a threshold.
852      * @hide
853      */
854     @UnsupportedAppUsage
getTotalBytes()855     public long getTotalBytes() {
856         final Entry entry = getTotal(null);
857         return entry.rxBytes + entry.txBytes;
858     }
859 
860     /**
861      * Return total of all fields represented by this snapshot object.
862      * @hide
863      */
864     @UnsupportedAppUsage
getTotal(Entry recycle)865     public Entry getTotal(Entry recycle) {
866         return getTotal(recycle, null, UID_ALL, false);
867     }
868 
869     /**
870      * Return total of all fields represented by this snapshot object matching
871      * the requested {@link #uid}.
872      * @hide
873      */
874     @UnsupportedAppUsage
getTotal(Entry recycle, int limitUid)875     public Entry getTotal(Entry recycle, int limitUid) {
876         return getTotal(recycle, null, limitUid, false);
877     }
878 
879     /**
880      * Return total of all fields represented by this snapshot object matching
881      * the requested {@link #iface}.
882      * @hide
883      */
getTotal(Entry recycle, HashSet<String> limitIface)884     public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
885         return getTotal(recycle, limitIface, UID_ALL, false);
886     }
887 
888     /** @hide */
889     @UnsupportedAppUsage
getTotalIncludingTags(Entry recycle)890     public Entry getTotalIncludingTags(Entry recycle) {
891         return getTotal(recycle, null, UID_ALL, true);
892     }
893 
894     /**
895      * Return total of all fields represented by this snapshot object matching
896      * the requested {@link #iface} and {@link #uid}.
897      *
898      * @param limitIface Set of {@link #iface} to include in total; or {@code
899      *            null} to include all ifaces.
900      */
getTotal( Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags)901     private Entry getTotal(
902             Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) {
903         final Entry entry = recycle != null ? recycle : new Entry();
904 
905         entry.iface = IFACE_ALL;
906         entry.uid = limitUid;
907         entry.set = SET_ALL;
908         entry.tag = TAG_NONE;
909         entry.metered = METERED_ALL;
910         entry.roaming = ROAMING_ALL;
911         entry.defaultNetwork = DEFAULT_NETWORK_ALL;
912         entry.rxBytes = 0;
913         entry.rxPackets = 0;
914         entry.txBytes = 0;
915         entry.txPackets = 0;
916         entry.operations = 0;
917 
918         for (int i = 0; i < size; i++) {
919             final boolean matchesUid = (limitUid == UID_ALL) || (limitUid == uid[i]);
920             final boolean matchesIface = (limitIface == null) || (limitIface.contains(iface[i]));
921 
922             if (matchesUid && matchesIface) {
923                 // skip specific tags, since already counted in TAG_NONE
924                 if (tag[i] != TAG_NONE && !includeTags) continue;
925 
926                 entry.rxBytes += rxBytes[i];
927                 entry.rxPackets += rxPackets[i];
928                 entry.txBytes += txBytes[i];
929                 entry.txPackets += txPackets[i];
930                 entry.operations += operations[i];
931             }
932         }
933         return entry;
934     }
935 
936     /**
937      * Fast path for battery stats.
938      * @hide
939      */
getTotalPackets()940     public long getTotalPackets() {
941         long total = 0;
942         for (int i = size-1; i >= 0; i--) {
943             total += rxPackets[i] + txPackets[i];
944         }
945         return total;
946     }
947 
948     /**
949      * Subtract the given {@link NetworkStats}, effectively leaving the delta
950      * between two snapshots in time. Assumes that statistics rows collect over
951      * time, and that none of them have disappeared. This method does not mutate
952      * the referencing object.
953      *
954      * @return the delta between two objects.
955      */
subtract(@onNull NetworkStats right)956     public @NonNull NetworkStats subtract(@NonNull NetworkStats right) {
957         return subtract(this, right, null, null);
958     }
959 
960     /**
961      * Subtract the two given {@link NetworkStats} objects, returning the delta
962      * between two snapshots in time. Assumes that statistics rows collect over
963      * time, and that none of them have disappeared.
964      * <p>
965      * If counters have rolled backwards, they are clamped to {@code 0} and
966      * reported to the given {@link NonMonotonicObserver}.
967      * @hide
968      */
subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie)969     public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
970             NonMonotonicObserver<C> observer, C cookie) {
971         return subtract(left, right, observer, cookie, null);
972     }
973 
974     /**
975      * Subtract the two given {@link NetworkStats} objects, returning the delta
976      * between two snapshots in time. Assumes that statistics rows collect over
977      * time, and that none of them have disappeared.
978      * <p>
979      * If counters have rolled backwards, they are clamped to {@code 0} and
980      * reported to the given {@link NonMonotonicObserver}.
981      * <p>
982      * If <var>recycle</var> is supplied, this NetworkStats object will be
983      * reused (and returned) as the result if it is large enough to contain
984      * the data.
985      * @hide
986      */
subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle)987     public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
988             NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) {
989         long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
990         if (deltaRealtime < 0) {
991             if (observer != null) {
992                 observer.foundNonMonotonic(left, -1, right, -1, cookie);
993             }
994             deltaRealtime = 0;
995         }
996 
997         // result will have our rows, and elapsed time between snapshots
998         final Entry entry = new Entry();
999         final NetworkStats result;
1000         if (recycle != null && recycle.capacity >= left.size) {
1001             result = recycle;
1002             result.size = 0;
1003             result.elapsedRealtime = deltaRealtime;
1004         } else {
1005             result = new NetworkStats(deltaRealtime, left.size);
1006         }
1007         for (int i = 0; i < left.size; i++) {
1008             entry.iface = left.iface[i];
1009             entry.uid = left.uid[i];
1010             entry.set = left.set[i];
1011             entry.tag = left.tag[i];
1012             entry.metered = left.metered[i];
1013             entry.roaming = left.roaming[i];
1014             entry.defaultNetwork = left.defaultNetwork[i];
1015             entry.rxBytes = left.rxBytes[i];
1016             entry.rxPackets = left.rxPackets[i];
1017             entry.txBytes = left.txBytes[i];
1018             entry.txPackets = left.txPackets[i];
1019             entry.operations = left.operations[i];
1020 
1021             // find remote row that matches, and subtract
1022             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
1023                     entry.metered, entry.roaming, entry.defaultNetwork, i);
1024             if (j != -1) {
1025                 // Found matching row, subtract remote value.
1026                 entry.rxBytes -= right.rxBytes[j];
1027                 entry.rxPackets -= right.rxPackets[j];
1028                 entry.txBytes -= right.txBytes[j];
1029                 entry.txPackets -= right.txPackets[j];
1030                 entry.operations -= right.operations[j];
1031             }
1032 
1033             if (entry.isNegative()) {
1034                 if (observer != null) {
1035                     observer.foundNonMonotonic(left, i, right, j, cookie);
1036                 }
1037                 entry.rxBytes = Math.max(entry.rxBytes, 0);
1038                 entry.rxPackets = Math.max(entry.rxPackets, 0);
1039                 entry.txBytes = Math.max(entry.txBytes, 0);
1040                 entry.txPackets = Math.max(entry.txPackets, 0);
1041                 entry.operations = Math.max(entry.operations, 0);
1042             }
1043 
1044             result.insertEntry(entry);
1045         }
1046 
1047         return result;
1048     }
1049 
1050     /**
1051      * Calculate and apply adjustments to captured statistics for 464xlat traffic.
1052      *
1053      * <p>This mutates stacked traffic stats, to account for IPv4/IPv6 header size difference.
1054      *
1055      * <p>UID stats, which are only accounted on the stacked interface, need to be increased
1056      * by 20 bytes/packet to account for translation overhead.
1057      *
1058      * <p>The potential additional overhead of 8 bytes/packet for ip fragments is ignored.
1059      *
1060      * <p>Interface stats need to sum traffic on both stacked and base interface because:
1061      *   - eBPF offloaded packets appear only on the stacked interface
1062      *   - Non-offloaded ingress packets appear only on the stacked interface
1063      *     (due to iptables raw PREROUTING drop rules)
1064      *   - Non-offloaded egress packets appear only on the stacked interface
1065      *     (due to ignoring traffic from clat daemon by uid match)
1066      * (and of course the 20 bytes/packet overhead needs to be applied to stacked interface stats)
1067      *
1068      * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
1069      * {@code ConcurrentHashMap}
1070      * @param baseTraffic Traffic on the base interfaces. Will be mutated.
1071      * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
1072      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
1073      * @hide
1074      */
apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic, Map<String, String> stackedIfaces)1075     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
1076             NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
1077         // For recycling
1078         Entry entry = null;
1079         for (int i = 0; i < stackedTraffic.size; i++) {
1080             entry = stackedTraffic.getValues(i, entry);
1081             if (entry == null) continue;
1082             if (entry.iface == null) continue;
1083             if (!entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) continue;
1084 
1085             // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
1086             // sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after
1087             // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
1088             // difference for all packets (http://b/12249687, http:/b/33681750).
1089             //
1090             // Note: this doesn't account for LRO/GRO/GSO/TSO (ie. >mtu) traffic correctly, nor
1091             // does it correctly account for the 8 extra bytes in the IPv6 fragmentation header.
1092             //
1093             // While the ebpf code path does try to simulate proper post segmentation packet
1094             // counts, we have nothing of the sort of xt_qtaguid stats.
1095             entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
1096             entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
1097             stackedTraffic.setValues(i, entry);
1098         }
1099     }
1100 
1101     /**
1102      * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
1103      *
1104      * <p>This mutates the object this method is called on. Equivalent to calling
1105      * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
1106      * base and stacked traffic.
1107      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
1108      * @hide
1109      */
apply464xlatAdjustments(Map<String, String> stackedIfaces)1110     public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
1111         apply464xlatAdjustments(this, this, stackedIfaces);
1112     }
1113 
1114     /**
1115      * Return total statistics grouped by {@link #iface}; doesn't mutate the
1116      * original structure.
1117      * @hide
1118      */
groupedByIface()1119     public NetworkStats groupedByIface() {
1120         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
1121 
1122         final Entry entry = new Entry();
1123         entry.uid = UID_ALL;
1124         entry.set = SET_ALL;
1125         entry.tag = TAG_NONE;
1126         entry.metered = METERED_ALL;
1127         entry.roaming = ROAMING_ALL;
1128         entry.defaultNetwork = DEFAULT_NETWORK_ALL;
1129         entry.operations = 0L;
1130 
1131         for (int i = 0; i < size; i++) {
1132             // skip specific tags, since already counted in TAG_NONE
1133             if (tag[i] != TAG_NONE) continue;
1134 
1135             entry.iface = iface[i];
1136             entry.rxBytes = rxBytes[i];
1137             entry.rxPackets = rxPackets[i];
1138             entry.txBytes = txBytes[i];
1139             entry.txPackets = txPackets[i];
1140             stats.combineValues(entry);
1141         }
1142 
1143         return stats;
1144     }
1145 
1146     /**
1147      * Return total statistics grouped by {@link #uid}; doesn't mutate the
1148      * original structure.
1149      * @hide
1150      */
groupedByUid()1151     public NetworkStats groupedByUid() {
1152         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
1153 
1154         final Entry entry = new Entry();
1155         entry.iface = IFACE_ALL;
1156         entry.set = SET_ALL;
1157         entry.tag = TAG_NONE;
1158         entry.metered = METERED_ALL;
1159         entry.roaming = ROAMING_ALL;
1160         entry.defaultNetwork = DEFAULT_NETWORK_ALL;
1161 
1162         for (int i = 0; i < size; i++) {
1163             // skip specific tags, since already counted in TAG_NONE
1164             if (tag[i] != TAG_NONE) continue;
1165 
1166             entry.uid = uid[i];
1167             entry.rxBytes = rxBytes[i];
1168             entry.rxPackets = rxPackets[i];
1169             entry.txBytes = txBytes[i];
1170             entry.txPackets = txPackets[i];
1171             entry.operations = operations[i];
1172             stats.combineValues(entry);
1173         }
1174 
1175         return stats;
1176     }
1177 
1178     /**
1179      * Remove all rows that match one of specified UIDs.
1180      * This mutates the original structure in place.
1181      * @hide
1182      */
removeUids(int[] uids)1183     public void removeUids(int[] uids) {
1184         filter(e -> !ArrayUtils.contains(uids, e.uid));
1185     }
1186 
1187     /**
1188      * Remove all rows that match one of specified UIDs.
1189      * @return the result object.
1190      * @hide
1191      */
1192     @NonNull
removeEmptyEntries()1193     public NetworkStats removeEmptyEntries() {
1194         final NetworkStats ret = this.clone();
1195         ret.filter(e -> e.rxBytes != 0 || e.rxPackets != 0 || e.txBytes != 0 || e.txPackets != 0
1196                 || e.operations != 0);
1197         return ret;
1198     }
1199 
1200     /**
1201      * Only keep entries that match all specified filters.
1202      *
1203      * <p>This mutates the original structure in place. After this method is called,
1204      * size is the number of matching entries, and capacity is the previous capacity.
1205      * @param limitUid UID to filter for, or {@link #UID_ALL}.
1206      * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
1207      * @param limitTag Tag to filter for, or {@link #TAG_ALL}.
1208      * @hide
1209      */
filter(int limitUid, String[] limitIfaces, int limitTag)1210     public void filter(int limitUid, String[] limitIfaces, int limitTag) {
1211         if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
1212             return;
1213         }
1214         filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
1215                 && (limitTag == TAG_ALL || limitTag == e.tag)
1216                 && (limitIfaces == INTERFACES_ALL
1217                     || ArrayUtils.contains(limitIfaces, e.iface)));
1218     }
1219 
1220     /**
1221      * Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}.
1222      *
1223      * <p>This mutates the original structure in place.
1224      * @hide
1225      */
filterDebugEntries()1226     public void filterDebugEntries() {
1227         filter(e -> e.set < SET_DEBUG_START);
1228     }
1229 
filter(Predicate<Entry> predicate)1230     private void filter(Predicate<Entry> predicate) {
1231         Entry entry = new Entry();
1232         int nextOutputEntry = 0;
1233         for (int i = 0; i < size; i++) {
1234             entry = getValues(i, entry);
1235             if (predicate.test(entry)) {
1236                 if (nextOutputEntry != i) {
1237                     setValues(nextOutputEntry, entry);
1238                 }
1239                 nextOutputEntry++;
1240             }
1241         }
1242         size = nextOutputEntry;
1243     }
1244 
1245     /** @hide */
dump(String prefix, PrintWriter pw)1246     public void dump(String prefix, PrintWriter pw) {
1247         pw.print(prefix);
1248         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
1249         for (int i = 0; i < size; i++) {
1250             pw.print(prefix);
1251             pw.print("  ["); pw.print(i); pw.print("]");
1252             pw.print(" iface="); pw.print(iface[i]);
1253             pw.print(" uid="); pw.print(uid[i]);
1254             pw.print(" set="); pw.print(setToString(set[i]));
1255             pw.print(" tag="); pw.print(tagToString(tag[i]));
1256             pw.print(" metered="); pw.print(meteredToString(metered[i]));
1257             pw.print(" roaming="); pw.print(roamingToString(roaming[i]));
1258             pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i]));
1259             pw.print(" rxBytes="); pw.print(rxBytes[i]);
1260             pw.print(" rxPackets="); pw.print(rxPackets[i]);
1261             pw.print(" txBytes="); pw.print(txBytes[i]);
1262             pw.print(" txPackets="); pw.print(txPackets[i]);
1263             pw.print(" operations="); pw.println(operations[i]);
1264         }
1265     }
1266 
1267     /**
1268      * Return text description of {@link #set} value.
1269      * @hide
1270      */
setToString(int set)1271     public static String setToString(int set) {
1272         switch (set) {
1273             case SET_ALL:
1274                 return "ALL";
1275             case SET_DEFAULT:
1276                 return "DEFAULT";
1277             case SET_FOREGROUND:
1278                 return "FOREGROUND";
1279             case SET_DBG_VPN_IN:
1280                 return "DBG_VPN_IN";
1281             case SET_DBG_VPN_OUT:
1282                 return "DBG_VPN_OUT";
1283             default:
1284                 return "UNKNOWN";
1285         }
1286     }
1287 
1288     /**
1289      * Return text description of {@link #set} value.
1290      * @hide
1291      */
setToCheckinString(int set)1292     public static String setToCheckinString(int set) {
1293         switch (set) {
1294             case SET_ALL:
1295                 return "all";
1296             case SET_DEFAULT:
1297                 return "def";
1298             case SET_FOREGROUND:
1299                 return "fg";
1300             case SET_DBG_VPN_IN:
1301                 return "vpnin";
1302             case SET_DBG_VPN_OUT:
1303                 return "vpnout";
1304             default:
1305                 return "unk";
1306         }
1307     }
1308 
1309     /**
1310      * @return true if the querySet matches the dataSet.
1311      * @hide
1312      */
setMatches(int querySet, int dataSet)1313     public static boolean setMatches(int querySet, int dataSet) {
1314         if (querySet == dataSet) {
1315             return true;
1316         }
1317         // SET_ALL matches all non-debugging sets.
1318         return querySet == SET_ALL && dataSet < SET_DEBUG_START;
1319     }
1320 
1321     /**
1322      * Return text description of {@link #tag} value.
1323      * @hide
1324      */
tagToString(int tag)1325     public static String tagToString(int tag) {
1326         return "0x" + Integer.toHexString(tag);
1327     }
1328 
1329     /**
1330      * Return text description of {@link #metered} value.
1331      * @hide
1332      */
meteredToString(int metered)1333     public static String meteredToString(int metered) {
1334         switch (metered) {
1335             case METERED_ALL:
1336                 return "ALL";
1337             case METERED_NO:
1338                 return "NO";
1339             case METERED_YES:
1340                 return "YES";
1341             default:
1342                 return "UNKNOWN";
1343         }
1344     }
1345 
1346     /**
1347      * Return text description of {@link #roaming} value.
1348      * @hide
1349      */
roamingToString(int roaming)1350     public static String roamingToString(int roaming) {
1351         switch (roaming) {
1352             case ROAMING_ALL:
1353                 return "ALL";
1354             case ROAMING_NO:
1355                 return "NO";
1356             case ROAMING_YES:
1357                 return "YES";
1358             default:
1359                 return "UNKNOWN";
1360         }
1361     }
1362 
1363     /**
1364      * Return text description of {@link #defaultNetwork} value.
1365      * @hide
1366      */
defaultNetworkToString(int defaultNetwork)1367     public static String defaultNetworkToString(int defaultNetwork) {
1368         switch (defaultNetwork) {
1369             case DEFAULT_NETWORK_ALL:
1370                 return "ALL";
1371             case DEFAULT_NETWORK_NO:
1372                 return "NO";
1373             case DEFAULT_NETWORK_YES:
1374                 return "YES";
1375             default:
1376                 return "UNKNOWN";
1377         }
1378     }
1379 
1380     /** @hide */
1381     @Override
toString()1382     public String toString() {
1383         final CharArrayWriter writer = new CharArrayWriter();
1384         dump("", new PrintWriter(writer));
1385         return writer.toString();
1386     }
1387 
1388     @Override
describeContents()1389     public int describeContents() {
1390         return 0;
1391     }
1392 
1393     public static final @NonNull Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
1394         @Override
1395         public NetworkStats createFromParcel(Parcel in) {
1396             return new NetworkStats(in);
1397         }
1398 
1399         @Override
1400         public NetworkStats[] newArray(int size) {
1401             return new NetworkStats[size];
1402         }
1403     };
1404 
1405     /** @hide */
1406     public interface NonMonotonicObserver<C> {
foundNonMonotonic( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie)1407         public void foundNonMonotonic(
1408                 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
foundNonMonotonic( NetworkStats stats, int statsIndex, C cookie)1409         public void foundNonMonotonic(
1410                 NetworkStats stats, int statsIndex, C cookie);
1411     }
1412 
1413     /**
1414      * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
1415      *
1416      * <p>This method should only be called on delta NetworkStats. Do not call this method on a
1417      * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may change
1418      * over time.
1419      *
1420      * <p>This method performs adjustments for one active VPN package and one VPN iface at a time.
1421      *
1422      * @param tunUid uid of the VPN application
1423      * @param tunIface iface of the vpn tunnel
1424      * @param underlyingIfaces underlying network ifaces used by the VPN application
1425      * @hide
1426      */
migrateTun(int tunUid, @NonNull String tunIface, @NonNull List<String> underlyingIfaces)1427     public void migrateTun(int tunUid, @NonNull String tunIface,
1428             @NonNull List<String> underlyingIfaces) {
1429         // Combined usage by all apps using VPN.
1430         final Entry tunIfaceTotal = new Entry();
1431         // Usage by VPN, grouped by its {@code underlyingIfaces}.
1432         final Entry[] perInterfaceTotal = new Entry[underlyingIfaces.size()];
1433         // Usage by VPN, summed across all its {@code underlyingIfaces}.
1434         final Entry underlyingIfacesTotal = new Entry();
1435 
1436         for (int i = 0; i < perInterfaceTotal.length; i++) {
1437             perInterfaceTotal[i] = new Entry();
1438         }
1439 
1440         tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal,
1441                 underlyingIfacesTotal);
1442 
1443         // If tunIface < underlyingIfacesTotal, it leaves the overhead traffic in the VPN app.
1444         // If tunIface > underlyingIfacesTotal, the VPN app doesn't get credit for data compression.
1445         // Negative stats should be avoided.
1446         final Entry[] moved =
1447                 addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal,
1448                         perInterfaceTotal, underlyingIfacesTotal);
1449         deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved);
1450     }
1451 
1452     /**
1453      * Initializes the data used by the migrateTun() method.
1454      *
1455      * <p>This is the first pass iteration which does the following work:
1456      *
1457      * <ul>
1458      *   <li>Adds up all the traffic through the tunUid's underlyingIfaces (both foreground and
1459      *       background).
1460      *   <li>Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
1461      * </ul>
1462      *
1463      * @param tunUid uid of the VPN application
1464      * @param tunIface iface of the vpn tunnel
1465      * @param underlyingIfaces underlying network ifaces used by the VPN application
1466      * @param tunIfaceTotal output parameter; combined data usage by all apps using VPN
1467      * @param perInterfaceTotal output parameter; data usage by VPN app, grouped by its {@code
1468      *     underlyingIfaces}
1469      * @param underlyingIfacesTotal output parameter; data usage by VPN, summed across all of its
1470      *     {@code underlyingIfaces}
1471      */
tunAdjustmentInit(int tunUid, @NonNull String tunIface, @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal, @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal)1472     private void tunAdjustmentInit(int tunUid, @NonNull String tunIface,
1473             @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal,
1474             @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
1475         final Entry recycle = new Entry();
1476         for (int i = 0; i < size; i++) {
1477             getValues(i, recycle);
1478             if (recycle.uid == UID_ALL) {
1479                 throw new IllegalStateException(
1480                         "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
1481             }
1482             if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
1483                 throw new IllegalStateException(
1484                         "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
1485             }
1486             if (recycle.tag != TAG_NONE) {
1487                 // TODO(b/123666283): Take all tags for tunUid into account.
1488                 continue;
1489             }
1490 
1491             if (tunUid == Process.SYSTEM_UID) {
1492                 // Kernel-based VPN or VCN, traffic sent by apps on the VPN/VCN network
1493                 //
1494                 // Since the data is not UID-accounted on underlying networks, just use VPN/VCN
1495                 // network usage as ground truth. Encrypted traffic on the underlying networks will
1496                 // never be processed here because encrypted traffic on the underlying interfaces
1497                 // is not present in UID stats, and this method is only called on UID stats.
1498                 if (tunIface.equals(recycle.iface)) {
1499                     tunIfaceTotal.add(recycle);
1500                     underlyingIfacesTotal.add(recycle);
1501 
1502                     // In steady state, there should always be one network, but edge cases may
1503                     // result in the network being null (network lost), and thus no underlying
1504                     // ifaces is possible.
1505                     if (perInterfaceTotal.length > 0) {
1506                         // While platform VPNs and VCNs have exactly one underlying network, that
1507                         // network may have multiple interfaces (eg for 464xlat). This layer does
1508                         // not have the required information to identify which of the interfaces
1509                         // were used. Select "any" of the interfaces. Since overhead is already
1510                         // lost, this number is an approximation anyways.
1511                         perInterfaceTotal[0].add(recycle);
1512                     }
1513                 }
1514             } else if (recycle.uid == tunUid) {
1515                 // VpnService VPN, traffic sent by the VPN app over underlying networks
1516                 for (int j = 0; j < underlyingIfaces.size(); j++) {
1517                     if (Objects.equals(underlyingIfaces.get(j), recycle.iface)) {
1518                         perInterfaceTotal[j].add(recycle);
1519                         underlyingIfacesTotal.add(recycle);
1520                         break;
1521                     }
1522                 }
1523             } else if (tunIface.equals(recycle.iface)) {
1524                 // VpnService VPN; traffic sent by apps on the VPN network
1525                 tunIfaceTotal.add(recycle);
1526             }
1527         }
1528     }
1529 
1530     /**
1531      * Distributes traffic across apps that are using given {@code tunIface}, and returns the total
1532      * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}.
1533      *
1534      * @param tunUid uid of the VPN application
1535      * @param tunIface iface of the vpn tunnel
1536      * @param underlyingIfaces underlying network ifaces used by the VPN application
1537      * @param tunIfaceTotal combined data usage across all apps using {@code tunIface}
1538      * @param perInterfaceTotal data usage by VPN app, grouped by its {@code underlyingIfaces}
1539      * @param underlyingIfacesTotal data usage by VPN, summed across all of its {@code
1540      *     underlyingIfaces}
1541      */
addTrafficToApplications(int tunUid, @NonNull String tunIface, @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal, @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal)1542     private Entry[] addTrafficToApplications(int tunUid, @NonNull String tunIface,
1543             @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal,
1544             @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
1545         // Traffic that should be moved off of each underlying interface for tunUid (see
1546         // deductTrafficFromVpnApp below).
1547         final Entry[] moved = new Entry[underlyingIfaces.size()];
1548         for (int i = 0; i < underlyingIfaces.size(); i++) {
1549             moved[i] = new Entry();
1550         }
1551 
1552         final Entry tmpEntry = new Entry();
1553         final int origSize = size;
1554         for (int i = 0; i < origSize; i++) {
1555             if (!Objects.equals(iface[i], tunIface)) {
1556                 // Consider only entries that go onto the VPN interface.
1557                 continue;
1558             }
1559 
1560             if (uid[i] == tunUid && tunUid != Process.SYSTEM_UID) {
1561                 // Exclude VPN app from the redistribution, as it can choose to create packet
1562                 // streams by writing to itself.
1563                 //
1564                 // However, for platform VPNs, do not exclude the system's usage of the VPN network,
1565                 // since it is never local-only, and never double counted
1566                 continue;
1567             }
1568             tmpEntry.uid = uid[i];
1569             tmpEntry.tag = tag[i];
1570             tmpEntry.metered = metered[i];
1571             tmpEntry.roaming = roaming[i];
1572             tmpEntry.defaultNetwork = defaultNetwork[i];
1573 
1574             // In a first pass, compute this entry's total share of data across all
1575             // underlyingIfaces. This is computed on the basis of the share of this entry's usage
1576             // over tunIface.
1577             // TODO: Consider refactoring first pass into a separate helper method.
1578             long totalRxBytes = 0;
1579             if (tunIfaceTotal.rxBytes > 0) {
1580                 // Note - The multiplication below should not overflow since NetworkStatsService
1581                 // processes this every time device has transmitted/received amount equivalent to
1582                 // global threshold alert (~ 2MB) across all interfaces.
1583                 final long rxBytesAcrossUnderlyingIfaces =
1584                         underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
1585                 // app must not be blamed for more than it consumed on tunIface
1586                 totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces);
1587             }
1588             long totalRxPackets = 0;
1589             if (tunIfaceTotal.rxPackets > 0) {
1590                 final long rxPacketsAcrossUnderlyingIfaces =
1591                         underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
1592                 totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces);
1593             }
1594             long totalTxBytes = 0;
1595             if (tunIfaceTotal.txBytes > 0) {
1596                 final long txBytesAcrossUnderlyingIfaces =
1597                         underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
1598                 totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces);
1599             }
1600             long totalTxPackets = 0;
1601             if (tunIfaceTotal.txPackets > 0) {
1602                 final long txPacketsAcrossUnderlyingIfaces =
1603                         underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
1604                 totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces);
1605             }
1606             long totalOperations = 0;
1607             if (tunIfaceTotal.operations > 0) {
1608                 final long operationsAcrossUnderlyingIfaces =
1609                         underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations;
1610                 totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces);
1611             }
1612             // In a second pass, distribute these values across interfaces in the proportion that
1613             // each interface represents of the total traffic of the underlying interfaces.
1614             for (int j = 0; j < underlyingIfaces.size(); j++) {
1615                 tmpEntry.iface = underlyingIfaces.get(j);
1616                 tmpEntry.rxBytes = 0;
1617                 // Reset 'set' to correct value since it gets updated when adding debug info below.
1618                 tmpEntry.set = set[i];
1619                 if (underlyingIfacesTotal.rxBytes > 0) {
1620                     tmpEntry.rxBytes =
1621                             totalRxBytes
1622                                     * perInterfaceTotal[j].rxBytes
1623                                     / underlyingIfacesTotal.rxBytes;
1624                 }
1625                 tmpEntry.rxPackets = 0;
1626                 if (underlyingIfacesTotal.rxPackets > 0) {
1627                     tmpEntry.rxPackets =
1628                             totalRxPackets
1629                                     * perInterfaceTotal[j].rxPackets
1630                                     / underlyingIfacesTotal.rxPackets;
1631                 }
1632                 tmpEntry.txBytes = 0;
1633                 if (underlyingIfacesTotal.txBytes > 0) {
1634                     tmpEntry.txBytes =
1635                             totalTxBytes
1636                                     * perInterfaceTotal[j].txBytes
1637                                     / underlyingIfacesTotal.txBytes;
1638                 }
1639                 tmpEntry.txPackets = 0;
1640                 if (underlyingIfacesTotal.txPackets > 0) {
1641                     tmpEntry.txPackets =
1642                             totalTxPackets
1643                                     * perInterfaceTotal[j].txPackets
1644                                     / underlyingIfacesTotal.txPackets;
1645                 }
1646                 tmpEntry.operations = 0;
1647                 if (underlyingIfacesTotal.operations > 0) {
1648                     tmpEntry.operations =
1649                             totalOperations
1650                                     * perInterfaceTotal[j].operations
1651                                     / underlyingIfacesTotal.operations;
1652                 }
1653                 // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying
1654                 // interface. Add that data usage to this object.
1655                 combineValues(tmpEntry);
1656                 if (tag[i] == TAG_NONE) {
1657                     // Add the migrated data to moved so it is deducted from the VPN app later.
1658                     moved[j].add(tmpEntry);
1659                     // Add debug info
1660                     tmpEntry.set = SET_DBG_VPN_IN;
1661                     combineValues(tmpEntry);
1662                 }
1663             }
1664         }
1665         return moved;
1666     }
1667 
deductTrafficFromVpnApp( int tunUid, @NonNull List<String> underlyingIfaces, @NonNull Entry[] moved)1668     private void deductTrafficFromVpnApp(
1669             int tunUid,
1670             @NonNull List<String> underlyingIfaces,
1671             @NonNull Entry[] moved) {
1672         if (tunUid == Process.SYSTEM_UID) {
1673             // No traffic recorded on a per-UID basis for in-kernel VPN/VCNs over underlying
1674             // networks; thus no traffic to deduct.
1675             return;
1676         }
1677 
1678         for (int i = 0; i < underlyingIfaces.size(); i++) {
1679             moved[i].uid = tunUid;
1680             // Add debug info
1681             moved[i].set = SET_DBG_VPN_OUT;
1682             moved[i].tag = TAG_NONE;
1683             moved[i].iface = underlyingIfaces.get(i);
1684             moved[i].metered = METERED_ALL;
1685             moved[i].roaming = ROAMING_ALL;
1686             moved[i].defaultNetwork = DEFAULT_NETWORK_ALL;
1687             combineValues(moved[i]);
1688 
1689             // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
1690             // the TAG_NONE traffic.
1691             //
1692             // Relies on the fact that the underlying traffic only has state ROAMING_NO and
1693             // METERED_NO, which should be the case as it comes directly from the /proc file.
1694             // We only blend in the roaming data after applying these adjustments, by checking the
1695             // NetworkIdentity of the underlying iface.
1696             final int idxVpnBackground = findIndex(underlyingIfaces.get(i), tunUid, SET_DEFAULT,
1697                             TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
1698             if (idxVpnBackground != -1) {
1699                 // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed
1700                 // from foreground usage.
1701                 tunSubtract(idxVpnBackground, this, moved[i]);
1702             }
1703 
1704             final int idxVpnForeground = findIndex(underlyingIfaces.get(i), tunUid, SET_FOREGROUND,
1705                             TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
1706             if (idxVpnForeground != -1) {
1707                 tunSubtract(idxVpnForeground, this, moved[i]);
1708             }
1709         }
1710     }
1711 
tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right)1712     private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) {
1713         long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
1714         left.rxBytes[i] -= rxBytes;
1715         right.rxBytes -= rxBytes;
1716 
1717         long rxPackets = Math.min(left.rxPackets[i], right.rxPackets);
1718         left.rxPackets[i] -= rxPackets;
1719         right.rxPackets -= rxPackets;
1720 
1721         long txBytes = Math.min(left.txBytes[i], right.txBytes);
1722         left.txBytes[i] -= txBytes;
1723         right.txBytes -= txBytes;
1724 
1725         long txPackets = Math.min(left.txPackets[i], right.txPackets);
1726         left.txPackets[i] -= txPackets;
1727         right.txPackets -= txPackets;
1728     }
1729 }
1730