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