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