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