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.Objects;
     35 
     36 /**
     37  * Collection of active network statistics. Can contain summary details across
     38  * all interfaces, or details with per-UID granularity. Internally stores data
     39  * as a large table, closely matching {@code /proc/} data format. This structure
     40  * optimizes for rapid in-memory comparison, but consider using
     41  * {@link NetworkStatsHistory} when persisting.
     42  *
     43  * @hide
     44  */
     45 public class NetworkStats implements Parcelable {
     46     private static final String TAG = "NetworkStats";
     47     /** {@link #iface} value when interface details unavailable. */
     48     public static final String IFACE_ALL = null;
     49     /** {@link #uid} value when UID details unavailable. */
     50     public static final int UID_ALL = -1;
     51     /** {@link #tag} value matching any tag. */
     52     // TODO: Rename TAG_ALL to TAG_ANY.
     53     public static final int TAG_ALL = -1;
     54     /** {@link #set} value for all sets combined, not including debug sets. */
     55     public static final int SET_ALL = -1;
     56     /** {@link #set} value where background data is accounted. */
     57     public static final int SET_DEFAULT = 0;
     58     /** {@link #set} value where foreground data is accounted. */
     59     public static final int SET_FOREGROUND = 1;
     60     /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */
     61     public static final int SET_DEBUG_START = 1000;
     62     /** Debug {@link #set} value when the VPN stats are moved in. */
     63     public static final int SET_DBG_VPN_IN = 1001;
     64     /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
     65     public static final int SET_DBG_VPN_OUT = 1002;
     66 
     67     /** {@link #tag} value for total data across all tags. */
     68     // TODO: Rename TAG_NONE to TAG_ALL.
     69     public static final int TAG_NONE = 0;
     70 
     71     /** {@link #set} value for all roaming values. */
     72     public static final int ROAMING_ALL = -1;
     73     /** {@link #set} value where native, non-roaming data is accounted. */
     74     public static final int ROAMING_NO = 0;
     75     /** {@link #set} value where roaming data is accounted. */
     76     public static final int ROAMING_YES = 1;
     77 
     78     // TODO: move fields to "mVariable" notation
     79 
     80     /**
     81      * {@link SystemClock#elapsedRealtime()} timestamp when this data was
     82      * generated.
     83      */
     84     private long elapsedRealtime;
     85     private int size;
     86     private int capacity;
     87     private String[] iface;
     88     private int[] uid;
     89     private int[] set;
     90     private int[] tag;
     91     private int[] roaming;
     92     private long[] rxBytes;
     93     private long[] rxPackets;
     94     private long[] txBytes;
     95     private long[] txPackets;
     96     private long[] operations;
     97 
     98     public static class Entry {
     99         public String iface;
    100         public int uid;
    101         public int set;
    102         public int tag;
    103         /**
    104          * Note that this is only populated w/ the default value when read from /proc or written
    105          * to disk. We merge in the correct value when reporting this value to clients of
    106          * getSummary().
    107          */
    108         public int roaming;
    109         public long rxBytes;
    110         public long rxPackets;
    111         public long txBytes;
    112         public long txPackets;
    113         public long operations;
    114 
    115         public Entry() {
    116             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
    117         }
    118 
    119         public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
    120             this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
    121                     operations);
    122         }
    123 
    124         public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
    125                 long txBytes, long txPackets, long operations) {
    126             this(iface, uid, set, tag, ROAMING_NO, rxBytes, rxPackets, txBytes, txPackets,
    127                     operations);
    128         }
    129 
    130         public Entry(String iface, int uid, int set, int tag, int roaming, long rxBytes,
    131                 long rxPackets, long txBytes, long txPackets, long operations) {
    132             this.iface = iface;
    133             this.uid = uid;
    134             this.set = set;
    135             this.tag = tag;
    136             this.roaming = roaming;
    137             this.rxBytes = rxBytes;
    138             this.rxPackets = rxPackets;
    139             this.txBytes = txBytes;
    140             this.txPackets = txPackets;
    141             this.operations = operations;
    142         }
    143 
    144         public boolean isNegative() {
    145             return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
    146         }
    147 
    148         public boolean isEmpty() {
    149             return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
    150                     && operations == 0;
    151         }
    152 
    153         public void add(Entry another) {
    154             this.rxBytes += another.rxBytes;
    155             this.rxPackets += another.rxPackets;
    156             this.txBytes += another.txBytes;
    157             this.txPackets += another.txPackets;
    158             this.operations += another.operations;
    159         }
    160 
    161         @Override
    162         public String toString() {
    163             final StringBuilder builder = new StringBuilder();
    164             builder.append("iface=").append(iface);
    165             builder.append(" uid=").append(uid);
    166             builder.append(" set=").append(setToString(set));
    167             builder.append(" tag=").append(tagToString(tag));
    168             builder.append(" roaming=").append(roamingToString(roaming));
    169             builder.append(" rxBytes=").append(rxBytes);
    170             builder.append(" rxPackets=").append(rxPackets);
    171             builder.append(" txBytes=").append(txBytes);
    172             builder.append(" txPackets=").append(txPackets);
    173             builder.append(" operations=").append(operations);
    174             return builder.toString();
    175         }
    176 
    177         @Override
    178         public boolean equals(Object o) {
    179             if (o instanceof Entry) {
    180                 final Entry e = (Entry) o;
    181                 return uid == e.uid && set == e.set && tag == e.tag && roaming == e.roaming
    182                         && rxBytes == e.rxBytes && rxPackets == e.rxPackets && txBytes == e.txBytes
    183                         && txPackets == e.txPackets && operations == e.operations
    184                         && iface.equals(e.iface);
    185             }
    186             return false;
    187         }
    188     }
    189 
    190     public NetworkStats(long elapsedRealtime, int initialSize) {
    191         this.elapsedRealtime = elapsedRealtime;
    192         this.size = 0;
    193         if (initialSize >= 0) {
    194             this.capacity = initialSize;
    195             this.iface = new String[initialSize];
    196             this.uid = new int[initialSize];
    197             this.set = new int[initialSize];
    198             this.tag = new int[initialSize];
    199             this.roaming = new int[initialSize];
    200             this.rxBytes = new long[initialSize];
    201             this.rxPackets = new long[initialSize];
    202             this.txBytes = new long[initialSize];
    203             this.txPackets = new long[initialSize];
    204             this.operations = new long[initialSize];
    205         } else {
    206             // Special case for use by NetworkStatsFactory to start out *really* empty.
    207             this.capacity = 0;
    208             this.iface = EmptyArray.STRING;
    209             this.uid = EmptyArray.INT;
    210             this.set = EmptyArray.INT;
    211             this.tag = EmptyArray.INT;
    212             this.roaming = EmptyArray.INT;
    213             this.rxBytes = EmptyArray.LONG;
    214             this.rxPackets = EmptyArray.LONG;
    215             this.txBytes = EmptyArray.LONG;
    216             this.txPackets = EmptyArray.LONG;
    217             this.operations = EmptyArray.LONG;
    218         }
    219     }
    220 
    221     public NetworkStats(Parcel parcel) {
    222         elapsedRealtime = parcel.readLong();
    223         size = parcel.readInt();
    224         capacity = parcel.readInt();
    225         iface = parcel.createStringArray();
    226         uid = parcel.createIntArray();
    227         set = parcel.createIntArray();
    228         tag = parcel.createIntArray();
    229         roaming = parcel.createIntArray();
    230         rxBytes = parcel.createLongArray();
    231         rxPackets = parcel.createLongArray();
    232         txBytes = parcel.createLongArray();
    233         txPackets = parcel.createLongArray();
    234         operations = parcel.createLongArray();
    235     }
    236 
    237     @Override
    238     public void writeToParcel(Parcel dest, int flags) {
    239         dest.writeLong(elapsedRealtime);
    240         dest.writeInt(size);
    241         dest.writeInt(capacity);
    242         dest.writeStringArray(iface);
    243         dest.writeIntArray(uid);
    244         dest.writeIntArray(set);
    245         dest.writeIntArray(tag);
    246         dest.writeIntArray(roaming);
    247         dest.writeLongArray(rxBytes);
    248         dest.writeLongArray(rxPackets);
    249         dest.writeLongArray(txBytes);
    250         dest.writeLongArray(txPackets);
    251         dest.writeLongArray(operations);
    252     }
    253 
    254     @Override
    255     public NetworkStats clone() {
    256         final NetworkStats clone = new NetworkStats(elapsedRealtime, size);
    257         NetworkStats.Entry entry = null;
    258         for (int i = 0; i < size; i++) {
    259             entry = getValues(i, entry);
    260             clone.addValues(entry);
    261         }
    262         return clone;
    263     }
    264 
    265     @VisibleForTesting
    266     public NetworkStats addIfaceValues(
    267             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
    268         return addValues(
    269                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
    270     }
    271 
    272     @VisibleForTesting
    273     public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes,
    274             long rxPackets, long txBytes, long txPackets, long operations) {
    275         return addValues(new Entry(
    276                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
    277     }
    278 
    279     @VisibleForTesting
    280     public NetworkStats addValues(String iface, int uid, int set, int tag, int roaming,
    281             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
    282         return addValues(new Entry(
    283                 iface, uid, set, tag, roaming, rxBytes, rxPackets, txBytes, txPackets, operations));
    284     }
    285 
    286     /**
    287      * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
    288      * object can be recycled across multiple calls.
    289      */
    290     public NetworkStats addValues(Entry entry) {
    291         if (size >= capacity) {
    292             final int newLength = Math.max(size, 10) * 3 / 2;
    293             iface = Arrays.copyOf(iface, newLength);
    294             uid = Arrays.copyOf(uid, newLength);
    295             set = Arrays.copyOf(set, newLength);
    296             tag = Arrays.copyOf(tag, newLength);
    297             roaming = Arrays.copyOf(roaming, newLength);
    298             rxBytes = Arrays.copyOf(rxBytes, newLength);
    299             rxPackets = Arrays.copyOf(rxPackets, newLength);
    300             txBytes = Arrays.copyOf(txBytes, newLength);
    301             txPackets = Arrays.copyOf(txPackets, newLength);
    302             operations = Arrays.copyOf(operations, newLength);
    303             capacity = newLength;
    304         }
    305 
    306         iface[size] = entry.iface;
    307         uid[size] = entry.uid;
    308         set[size] = entry.set;
    309         tag[size] = entry.tag;
    310         roaming[size] = entry.roaming;
    311         rxBytes[size] = entry.rxBytes;
    312         rxPackets[size] = entry.rxPackets;
    313         txBytes[size] = entry.txBytes;
    314         txPackets[size] = entry.txPackets;
    315         operations[size] = entry.operations;
    316         size++;
    317 
    318         return this;
    319     }
    320 
    321     /**
    322      * Return specific stats entry.
    323      */
    324     public Entry getValues(int i, Entry recycle) {
    325         final Entry entry = recycle != null ? recycle : new Entry();
    326         entry.iface = iface[i];
    327         entry.uid = uid[i];
    328         entry.set = set[i];
    329         entry.tag = tag[i];
    330         entry.roaming = roaming[i];
    331         entry.rxBytes = rxBytes[i];
    332         entry.rxPackets = rxPackets[i];
    333         entry.txBytes = txBytes[i];
    334         entry.txPackets = txPackets[i];
    335         entry.operations = operations[i];
    336         return entry;
    337     }
    338 
    339     public long getElapsedRealtime() {
    340         return elapsedRealtime;
    341     }
    342 
    343     public void setElapsedRealtime(long time) {
    344         elapsedRealtime = time;
    345     }
    346 
    347     /**
    348      * Return age of this {@link NetworkStats} object with respect to
    349      * {@link SystemClock#elapsedRealtime()}.
    350      */
    351     public long getElapsedRealtimeAge() {
    352         return SystemClock.elapsedRealtime() - elapsedRealtime;
    353     }
    354 
    355     public int size() {
    356         return size;
    357     }
    358 
    359     @VisibleForTesting
    360     public int internalSize() {
    361         return capacity;
    362     }
    363 
    364     @Deprecated
    365     public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
    366             long txBytes, long txPackets, long operations) {
    367         return combineValues(
    368                 iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes,
    369                 txPackets, operations);
    370     }
    371 
    372     public NetworkStats combineValues(String iface, int uid, int set, int tag,
    373             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
    374         return combineValues(new Entry(
    375                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
    376     }
    377 
    378     /**
    379      * Combine given values with an existing row, or create a new row if
    380      * {@link #findIndex(String, int, int, int, int)} is unable to find match. Can
    381      * also be used to subtract values from existing rows.
    382      */
    383     public NetworkStats combineValues(Entry entry) {
    384         final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.roaming);
    385         if (i == -1) {
    386             // only create new entry when positive contribution
    387             addValues(entry);
    388         } else {
    389             rxBytes[i] += entry.rxBytes;
    390             rxPackets[i] += entry.rxPackets;
    391             txBytes[i] += entry.txBytes;
    392             txPackets[i] += entry.txPackets;
    393             operations[i] += entry.operations;
    394         }
    395         return this;
    396     }
    397 
    398     /**
    399      * Combine all values from another {@link NetworkStats} into this object.
    400      */
    401     public void combineAllValues(NetworkStats another) {
    402         NetworkStats.Entry entry = null;
    403         for (int i = 0; i < another.size; i++) {
    404             entry = another.getValues(i, entry);
    405             combineValues(entry);
    406         }
    407     }
    408 
    409     /**
    410      * Find first stats index that matches the requested parameters.
    411      */
    412     public int findIndex(String iface, int uid, int set, int tag, int roaming) {
    413         for (int i = 0; i < size; i++) {
    414             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
    415                     && roaming == this.roaming[i] && Objects.equals(iface, this.iface[i])) {
    416                 return i;
    417             }
    418         }
    419         return -1;
    420     }
    421 
    422     /**
    423      * Find first stats index that matches the requested parameters, starting
    424      * search around the hinted index as an optimization.
    425      */
    426     @VisibleForTesting
    427     public int findIndexHinted(String iface, int uid, int set, int tag, int roaming,
    428             int hintIndex) {
    429         for (int offset = 0; offset < size; offset++) {
    430             final int halfOffset = offset / 2;
    431 
    432             // search outwards from hint index, alternating forward and backward
    433             final int i;
    434             if (offset % 2 == 0) {
    435                 i = (hintIndex + halfOffset) % size;
    436             } else {
    437                 i = (size + hintIndex - halfOffset - 1) % size;
    438             }
    439 
    440             if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
    441                     && roaming == this.roaming[i] && Objects.equals(iface, this.iface[i])) {
    442                 return i;
    443             }
    444         }
    445         return -1;
    446     }
    447 
    448     /**
    449      * Splice in {@link #operations} from the given {@link NetworkStats} based
    450      * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
    451      * since operation counts are at data layer.
    452      */
    453     public void spliceOperationsFrom(NetworkStats stats) {
    454         for (int i = 0; i < size; i++) {
    455             final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], roaming[i]);
    456             if (j == -1) {
    457                 operations[i] = 0;
    458             } else {
    459                 operations[i] = stats.operations[j];
    460             }
    461         }
    462     }
    463 
    464     /**
    465      * Return list of unique interfaces known by this data structure.
    466      */
    467     public String[] getUniqueIfaces() {
    468         final HashSet<String> ifaces = new HashSet<String>();
    469         for (String iface : this.iface) {
    470             if (iface != IFACE_ALL) {
    471                 ifaces.add(iface);
    472             }
    473         }
    474         return ifaces.toArray(new String[ifaces.size()]);
    475     }
    476 
    477     /**
    478      * Return list of unique UIDs known by this data structure.
    479      */
    480     public int[] getUniqueUids() {
    481         final SparseBooleanArray uids = new SparseBooleanArray();
    482         for (int uid : this.uid) {
    483             uids.put(uid, true);
    484         }
    485 
    486         final int size = uids.size();
    487         final int[] result = new int[size];
    488         for (int i = 0; i < size; i++) {
    489             result[i] = uids.keyAt(i);
    490         }
    491         return result;
    492     }
    493 
    494     /**
    495      * Return total bytes represented by this snapshot object, usually used when
    496      * checking if a {@link #subtract(NetworkStats)} delta passes a threshold.
    497      */
    498     public long getTotalBytes() {
    499         final Entry entry = getTotal(null);
    500         return entry.rxBytes + entry.txBytes;
    501     }
    502 
    503     /**
    504      * Return total of all fields represented by this snapshot object.
    505      */
    506     public Entry getTotal(Entry recycle) {
    507         return getTotal(recycle, null, UID_ALL, false);
    508     }
    509 
    510     /**
    511      * Return total of all fields represented by this snapshot object matching
    512      * the requested {@link #uid}.
    513      */
    514     public Entry getTotal(Entry recycle, int limitUid) {
    515         return getTotal(recycle, null, limitUid, false);
    516     }
    517 
    518     /**
    519      * Return total of all fields represented by this snapshot object matching
    520      * the requested {@link #iface}.
    521      */
    522     public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
    523         return getTotal(recycle, limitIface, UID_ALL, false);
    524     }
    525 
    526     public Entry getTotalIncludingTags(Entry recycle) {
    527         return getTotal(recycle, null, UID_ALL, true);
    528     }
    529 
    530     /**
    531      * Return total of all fields represented by this snapshot object matching
    532      * the requested {@link #iface} and {@link #uid}.
    533      *
    534      * @param limitIface Set of {@link #iface} to include in total; or {@code
    535      *            null} to include all ifaces.
    536      */
    537     private Entry getTotal(
    538             Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) {
    539         final Entry entry = recycle != null ? recycle : new Entry();
    540 
    541         entry.iface = IFACE_ALL;
    542         entry.uid = limitUid;
    543         entry.set = SET_ALL;
    544         entry.tag = TAG_NONE;
    545         entry.roaming = ROAMING_ALL;
    546         entry.rxBytes = 0;
    547         entry.rxPackets = 0;
    548         entry.txBytes = 0;
    549         entry.txPackets = 0;
    550         entry.operations = 0;
    551 
    552         for (int i = 0; i < size; i++) {
    553             final boolean matchesUid = (limitUid == UID_ALL) || (limitUid == uid[i]);
    554             final boolean matchesIface = (limitIface == null) || (limitIface.contains(iface[i]));
    555 
    556             if (matchesUid && matchesIface) {
    557                 // skip specific tags, since already counted in TAG_NONE
    558                 if (tag[i] != TAG_NONE && !includeTags) continue;
    559 
    560                 entry.rxBytes += rxBytes[i];
    561                 entry.rxPackets += rxPackets[i];
    562                 entry.txBytes += txBytes[i];
    563                 entry.txPackets += txPackets[i];
    564                 entry.operations += operations[i];
    565             }
    566         }
    567         return entry;
    568     }
    569 
    570     /**
    571      * Fast path for battery stats.
    572      */
    573     public long getTotalPackets() {
    574         long total = 0;
    575         for (int i = size-1; i >= 0; i--) {
    576             total += rxPackets[i] + txPackets[i];
    577         }
    578         return total;
    579     }
    580 
    581     /**
    582      * Subtract the given {@link NetworkStats}, effectively leaving the delta
    583      * between two snapshots in time. Assumes that statistics rows collect over
    584      * time, and that none of them have disappeared.
    585      */
    586     public NetworkStats subtract(NetworkStats right) {
    587         return subtract(this, right, null, null);
    588     }
    589 
    590     /**
    591      * Subtract the two given {@link NetworkStats} objects, returning the delta
    592      * between two snapshots in time. Assumes that statistics rows collect over
    593      * time, and that none of them have disappeared.
    594      * <p>
    595      * If counters have rolled backwards, they are clamped to {@code 0} and
    596      * reported to the given {@link NonMonotonicObserver}.
    597      */
    598     public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
    599             NonMonotonicObserver<C> observer, C cookie) {
    600         return subtract(left, right, observer, cookie, null);
    601     }
    602 
    603     /**
    604      * Subtract the two given {@link NetworkStats} objects, returning the delta
    605      * between two snapshots in time. Assumes that statistics rows collect over
    606      * time, and that none of them have disappeared.
    607      * <p>
    608      * If counters have rolled backwards, they are clamped to {@code 0} and
    609      * reported to the given {@link NonMonotonicObserver}.
    610      * <p>
    611      * If <var>recycle</var> is supplied, this NetworkStats object will be
    612      * reused (and returned) as the result if it is large enough to contain
    613      * the data.
    614      */
    615     public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
    616             NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) {
    617         long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
    618         if (deltaRealtime < 0) {
    619             if (observer != null) {
    620                 observer.foundNonMonotonic(left, -1, right, -1, cookie);
    621             }
    622             deltaRealtime = 0;
    623         }
    624 
    625         // result will have our rows, and elapsed time between snapshots
    626         final Entry entry = new Entry();
    627         final NetworkStats result;
    628         if (recycle != null && recycle.capacity >= left.size) {
    629             result = recycle;
    630             result.size = 0;
    631             result.elapsedRealtime = deltaRealtime;
    632         } else {
    633             result = new NetworkStats(deltaRealtime, left.size);
    634         }
    635         for (int i = 0; i < left.size; i++) {
    636             entry.iface = left.iface[i];
    637             entry.uid = left.uid[i];
    638             entry.set = left.set[i];
    639             entry.tag = left.tag[i];
    640             entry.roaming = left.roaming[i];
    641 
    642             // find remote row that matches, and subtract
    643             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
    644                     entry.roaming, i);
    645             if (j == -1) {
    646                 // newly appearing row, return entire value
    647                 entry.rxBytes = left.rxBytes[i];
    648                 entry.rxPackets = left.rxPackets[i];
    649                 entry.txBytes = left.txBytes[i];
    650                 entry.txPackets = left.txPackets[i];
    651                 entry.operations = left.operations[i];
    652             } else {
    653                 // existing row, subtract remote value
    654                 entry.rxBytes = left.rxBytes[i] - right.rxBytes[j];
    655                 entry.rxPackets = left.rxPackets[i] - right.rxPackets[j];
    656                 entry.txBytes = left.txBytes[i] - right.txBytes[j];
    657                 entry.txPackets = left.txPackets[i] - right.txPackets[j];
    658                 entry.operations = left.operations[i] - right.operations[j];
    659 
    660                 if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
    661                         || entry.txPackets < 0 || entry.operations < 0) {
    662                     if (observer != null) {
    663                         observer.foundNonMonotonic(left, i, right, j, cookie);
    664                     }
    665                     entry.rxBytes = Math.max(entry.rxBytes, 0);
    666                     entry.rxPackets = Math.max(entry.rxPackets, 0);
    667                     entry.txBytes = Math.max(entry.txBytes, 0);
    668                     entry.txPackets = Math.max(entry.txPackets, 0);
    669                     entry.operations = Math.max(entry.operations, 0);
    670                 }
    671             }
    672 
    673             result.addValues(entry);
    674         }
    675 
    676         return result;
    677     }
    678 
    679     /**
    680      * Return total statistics grouped by {@link #iface}; doesn't mutate the
    681      * original structure.
    682      */
    683     public NetworkStats groupedByIface() {
    684         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
    685 
    686         final Entry entry = new Entry();
    687         entry.uid = UID_ALL;
    688         entry.set = SET_ALL;
    689         entry.tag = TAG_NONE;
    690         entry.roaming = ROAMING_ALL;
    691         entry.operations = 0L;
    692 
    693         for (int i = 0; i < size; i++) {
    694             // skip specific tags, since already counted in TAG_NONE
    695             if (tag[i] != TAG_NONE) continue;
    696 
    697             entry.iface = iface[i];
    698             entry.rxBytes = rxBytes[i];
    699             entry.rxPackets = rxPackets[i];
    700             entry.txBytes = txBytes[i];
    701             entry.txPackets = txPackets[i];
    702             stats.combineValues(entry);
    703         }
    704 
    705         return stats;
    706     }
    707 
    708     /**
    709      * Return total statistics grouped by {@link #uid}; doesn't mutate the
    710      * original structure.
    711      */
    712     public NetworkStats groupedByUid() {
    713         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
    714 
    715         final Entry entry = new Entry();
    716         entry.iface = IFACE_ALL;
    717         entry.set = SET_ALL;
    718         entry.tag = TAG_NONE;
    719         entry.roaming = ROAMING_ALL;
    720 
    721         for (int i = 0; i < size; i++) {
    722             // skip specific tags, since already counted in TAG_NONE
    723             if (tag[i] != TAG_NONE) continue;
    724 
    725             entry.uid = uid[i];
    726             entry.rxBytes = rxBytes[i];
    727             entry.rxPackets = rxPackets[i];
    728             entry.txBytes = txBytes[i];
    729             entry.txPackets = txPackets[i];
    730             entry.operations = operations[i];
    731             stats.combineValues(entry);
    732         }
    733 
    734         return stats;
    735     }
    736 
    737     /**
    738      * Return all rows except those attributed to the requested UID; doesn't
    739      * mutate the original structure.
    740      */
    741     public NetworkStats withoutUids(int[] uids) {
    742         final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
    743 
    744         Entry entry = new Entry();
    745         for (int i = 0; i < size; i++) {
    746             entry = getValues(i, entry);
    747             if (!ArrayUtils.contains(uids, entry.uid)) {
    748                 stats.addValues(entry);
    749             }
    750         }
    751 
    752         return stats;
    753     }
    754 
    755     public void dump(String prefix, PrintWriter pw) {
    756         pw.print(prefix);
    757         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
    758         for (int i = 0; i < size; i++) {
    759             pw.print(prefix);
    760             pw.print("  ["); pw.print(i); pw.print("]");
    761             pw.print(" iface="); pw.print(iface[i]);
    762             pw.print(" uid="); pw.print(uid[i]);
    763             pw.print(" set="); pw.print(setToString(set[i]));
    764             pw.print(" tag="); pw.print(tagToString(tag[i]));
    765             pw.print(" roaming="); pw.print(roamingToString(roaming[i]));
    766             pw.print(" rxBytes="); pw.print(rxBytes[i]);
    767             pw.print(" rxPackets="); pw.print(rxPackets[i]);
    768             pw.print(" txBytes="); pw.print(txBytes[i]);
    769             pw.print(" txPackets="); pw.print(txPackets[i]);
    770             pw.print(" operations="); pw.println(operations[i]);
    771         }
    772     }
    773 
    774     /**
    775      * Return text description of {@link #set} value.
    776      */
    777     public static String setToString(int set) {
    778         switch (set) {
    779             case SET_ALL:
    780                 return "ALL";
    781             case SET_DEFAULT:
    782                 return "DEFAULT";
    783             case SET_FOREGROUND:
    784                 return "FOREGROUND";
    785             case SET_DBG_VPN_IN:
    786                 return "DBG_VPN_IN";
    787             case SET_DBG_VPN_OUT:
    788                 return "DBG_VPN_OUT";
    789             default:
    790                 return "UNKNOWN";
    791         }
    792     }
    793 
    794     /**
    795      * Return text description of {@link #set} value.
    796      */
    797     public static String setToCheckinString(int set) {
    798         switch (set) {
    799             case SET_ALL:
    800                 return "all";
    801             case SET_DEFAULT:
    802                 return "def";
    803             case SET_FOREGROUND:
    804                 return "fg";
    805             case SET_DBG_VPN_IN:
    806                 return "vpnin";
    807             case SET_DBG_VPN_OUT:
    808                 return "vpnout";
    809             default:
    810                 return "unk";
    811         }
    812     }
    813 
    814     /**
    815      * @return true if the querySet matches the dataSet.
    816      */
    817     public static boolean setMatches(int querySet, int dataSet) {
    818         if (querySet == dataSet) {
    819             return true;
    820         }
    821         // SET_ALL matches all non-debugging sets.
    822         return querySet == SET_ALL && dataSet < SET_DEBUG_START;
    823     }
    824 
    825     /**
    826      * Return text description of {@link #tag} value.
    827      */
    828     public static String tagToString(int tag) {
    829         return "0x" + Integer.toHexString(tag);
    830     }
    831 
    832     /**
    833      * Return text description of {@link #roaming} value.
    834      */
    835     public static String roamingToString(int roaming) {
    836         switch (roaming) {
    837             case ROAMING_ALL:
    838                 return "ALL";
    839             case ROAMING_NO:
    840                 return "NO";
    841             case ROAMING_YES:
    842                 return "YES";
    843             default:
    844                 return "UNKNOWN";
    845         }
    846     }
    847 
    848     @Override
    849     public String toString() {
    850         final CharArrayWriter writer = new CharArrayWriter();
    851         dump("", new PrintWriter(writer));
    852         return writer.toString();
    853     }
    854 
    855     @Override
    856     public int describeContents() {
    857         return 0;
    858     }
    859 
    860     public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
    861         @Override
    862         public NetworkStats createFromParcel(Parcel in) {
    863             return new NetworkStats(in);
    864         }
    865 
    866         @Override
    867         public NetworkStats[] newArray(int size) {
    868             return new NetworkStats[size];
    869         }
    870     };
    871 
    872     public interface NonMonotonicObserver<C> {
    873         public void foundNonMonotonic(
    874                 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
    875     }
    876 
    877     /**
    878      * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
    879      *
    880      * This method should only be called on delta NetworkStats. Do not call this method on a
    881      * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may
    882      * change over time.
    883      *
    884      * This method performs adjustments for one active VPN package and one VPN iface at a time.
    885      *
    886      * It is possible for the VPN software to use multiple underlying networks. This method
    887      * only migrates traffic for the primary underlying network.
    888      *
    889      * @param tunUid uid of the VPN application
    890      * @param tunIface iface of the vpn tunnel
    891      * @param underlyingIface the primary underlying network iface used by the VPN application
    892      * @return true if it successfully adjusts the accounting for VPN, false otherwise
    893      */
    894     public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) {
    895         Entry tunIfaceTotal = new Entry();
    896         Entry underlyingIfaceTotal = new Entry();
    897 
    898         tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal);
    899 
    900         // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app.
    901         // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression.
    902         // Negative stats should be avoided.
    903         Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal);
    904         if (pool.isEmpty()) {
    905             return true;
    906         }
    907         Entry moved =
    908                 addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
    909         deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
    910 
    911         if (!moved.isEmpty()) {
    912             Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved="
    913                     + moved);
    914             return false;
    915         }
    916         return true;
    917     }
    918 
    919     /**
    920      * Initializes the data used by the migrateTun() method.
    921      *
    922      * This is the first pass iteration which does the following work:
    923      * (1) Adds up all the traffic through the tunUid's underlyingIface
    924      *     (both foreground and background).
    925      * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
    926      */
    927     private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface,
    928             Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
    929         Entry recycle = new Entry();
    930         for (int i = 0; i < size; i++) {
    931             getValues(i, recycle);
    932             if (recycle.uid == UID_ALL) {
    933                 throw new IllegalStateException(
    934                         "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
    935             } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
    936                 throw new IllegalStateException(
    937                         "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
    938             }
    939 
    940             if (recycle.uid == tunUid && recycle.tag == TAG_NONE
    941                     && Objects.equals(underlyingIface, recycle.iface)) {
    942                 underlyingIfaceTotal.add(recycle);
    943             }
    944 
    945             if (recycle.uid != tunUid && recycle.tag == TAG_NONE
    946                     && Objects.equals(tunIface, recycle.iface)) {
    947                 // Add up all tunIface traffic excluding traffic from the vpn app itself.
    948                 tunIfaceTotal.add(recycle);
    949             }
    950         }
    951     }
    952 
    953     private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
    954         Entry pool = new Entry();
    955         pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes);
    956         pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets);
    957         pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes);
    958         pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets);
    959         pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations);
    960         return pool;
    961     }
    962 
    963     private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface,
    964             Entry tunIfaceTotal, Entry pool) {
    965         Entry moved = new Entry();
    966         Entry tmpEntry = new Entry();
    967         tmpEntry.iface = underlyingIface;
    968         for (int i = 0; i < size; i++) {
    969             // the vpn app is excluded from the redistribution but all moved traffic will be
    970             // deducted from the vpn app (see deductTrafficFromVpnApp below).
    971             if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) {
    972                 if (tunIfaceTotal.rxBytes > 0) {
    973                     tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
    974                 } else {
    975                     tmpEntry.rxBytes = 0;
    976                 }
    977                 if (tunIfaceTotal.rxPackets > 0) {
    978                     tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
    979                 } else {
    980                     tmpEntry.rxPackets = 0;
    981                 }
    982                 if (tunIfaceTotal.txBytes > 0) {
    983                     tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
    984                 } else {
    985                     tmpEntry.txBytes = 0;
    986                 }
    987                 if (tunIfaceTotal.txPackets > 0) {
    988                     tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
    989                 } else {
    990                     tmpEntry.txPackets = 0;
    991                 }
    992                 if (tunIfaceTotal.operations > 0) {
    993                     tmpEntry.operations =
    994                             pool.operations * operations[i] / tunIfaceTotal.operations;
    995                 } else {
    996                     tmpEntry.operations = 0;
    997                 }
    998                 tmpEntry.uid = uid[i];
    999                 tmpEntry.tag = tag[i];
   1000                 tmpEntry.set = set[i];
   1001                 tmpEntry.roaming = roaming[i];
   1002                 combineValues(tmpEntry);
   1003                 if (tag[i] == TAG_NONE) {
   1004                     moved.add(tmpEntry);
   1005                     // Add debug info
   1006                     tmpEntry.set = SET_DBG_VPN_IN;
   1007                     combineValues(tmpEntry);
   1008                 }
   1009             }
   1010         }
   1011         return moved;
   1012     }
   1013 
   1014     private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
   1015         // Add debug info
   1016         moved.uid = tunUid;
   1017         moved.set = SET_DBG_VPN_OUT;
   1018         moved.tag = TAG_NONE;
   1019         moved.iface = underlyingIface;
   1020         moved.roaming = ROAMING_ALL;
   1021         combineValues(moved);
   1022 
   1023         // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
   1024         // the TAG_NONE traffic.
   1025         //
   1026         // Relies on the fact that the underlying traffic only has state ROAMING_NO, which
   1027         // should be the case as it comes directly from the /proc file. We only blend in the
   1028         // roaming data after applying these adjustments, by checking the NetworkIdentity of the
   1029         // underlying iface.
   1030         int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
   1031                 ROAMING_NO);
   1032         if (idxVpnBackground != -1) {
   1033             tunSubtract(idxVpnBackground, this, moved);
   1034         }
   1035 
   1036         int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
   1037                 ROAMING_NO);
   1038         if (idxVpnForeground != -1) {
   1039             tunSubtract(idxVpnForeground, this, moved);
   1040         }
   1041     }
   1042 
   1043     private static void tunSubtract(int i, NetworkStats left, Entry right) {
   1044         long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
   1045         left.rxBytes[i] -= rxBytes;
   1046         right.rxBytes -= rxBytes;
   1047 
   1048         long rxPackets = Math.min(left.rxPackets[i], right.rxPackets);
   1049         left.rxPackets[i] -= rxPackets;
   1050         right.rxPackets -= rxPackets;
   1051 
   1052         long txBytes = Math.min(left.txBytes[i], right.txBytes);
   1053         left.txBytes[i] -= txBytes;
   1054         right.txBytes -= txBytes;
   1055 
   1056         long txPackets = Math.min(left.txPackets[i], right.txPackets);
   1057         left.txPackets[i] -= txPackets;
   1058         right.txPackets -= txPackets;
   1059     }
   1060 }
   1061