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