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 static android.net.NetworkStats.IFACE_ALL;
     20 import static android.net.NetworkStats.SET_DEFAULT;
     21 import static android.net.NetworkStats.TAG_NONE;
     22 import static android.net.NetworkStats.UID_ALL;
     23 import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray;
     24 import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLongArray;
     25 import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
     26 import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
     27 import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
     28 import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
     29 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
     30 
     31 import static com.android.internal.util.ArrayUtils.total;
     32 
     33 import android.os.Parcel;
     34 import android.os.Parcelable;
     35 import android.service.NetworkStatsHistoryBucketProto;
     36 import android.service.NetworkStatsHistoryProto;
     37 import android.util.MathUtils;
     38 import android.util.proto.ProtoOutputStream;
     39 
     40 import com.android.internal.util.IndentingPrintWriter;
     41 
     42 import libcore.util.EmptyArray;
     43 
     44 import java.io.CharArrayWriter;
     45 import java.io.DataInputStream;
     46 import java.io.DataOutputStream;
     47 import java.io.IOException;
     48 import java.io.PrintWriter;
     49 import java.net.ProtocolException;
     50 import java.util.Arrays;
     51 import java.util.Random;
     52 
     53 /**
     54  * Collection of historical network statistics, recorded into equally-sized
     55  * "buckets" in time. Internally it stores data in {@code long} series for more
     56  * efficient persistence.
     57  * <p>
     58  * Each bucket is defined by a {@link #bucketStart} timestamp, and lasts for
     59  * {@link #bucketDuration}. Internally assumes that {@link #bucketStart} is
     60  * sorted at all times.
     61  *
     62  * @hide
     63  */
     64 public class NetworkStatsHistory implements Parcelable {
     65     private static final int VERSION_INIT = 1;
     66     private static final int VERSION_ADD_PACKETS = 2;
     67     private static final int VERSION_ADD_ACTIVE = 3;
     68 
     69     public static final int FIELD_ACTIVE_TIME = 0x01;
     70     public static final int FIELD_RX_BYTES = 0x02;
     71     public static final int FIELD_RX_PACKETS = 0x04;
     72     public static final int FIELD_TX_BYTES = 0x08;
     73     public static final int FIELD_TX_PACKETS = 0x10;
     74     public static final int FIELD_OPERATIONS = 0x20;
     75 
     76     public static final int FIELD_ALL = 0xFFFFFFFF;
     77 
     78     private long bucketDuration;
     79     private int bucketCount;
     80     private long[] bucketStart;
     81     private long[] activeTime;
     82     private long[] rxBytes;
     83     private long[] rxPackets;
     84     private long[] txBytes;
     85     private long[] txPackets;
     86     private long[] operations;
     87     private long totalBytes;
     88 
     89     public static class Entry {
     90         public static final long UNKNOWN = -1;
     91 
     92         public long bucketDuration;
     93         public long bucketStart;
     94         public long activeTime;
     95         public long rxBytes;
     96         public long rxPackets;
     97         public long txBytes;
     98         public long txPackets;
     99         public long operations;
    100     }
    101 
    102     public NetworkStatsHistory(long bucketDuration) {
    103         this(bucketDuration, 10, FIELD_ALL);
    104     }
    105 
    106     public NetworkStatsHistory(long bucketDuration, int initialSize) {
    107         this(bucketDuration, initialSize, FIELD_ALL);
    108     }
    109 
    110     public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
    111         this.bucketDuration = bucketDuration;
    112         bucketStart = new long[initialSize];
    113         if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[initialSize];
    114         if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize];
    115         if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize];
    116         if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize];
    117         if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
    118         if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
    119         bucketCount = 0;
    120         totalBytes = 0;
    121     }
    122 
    123     public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
    124         this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
    125         recordEntireHistory(existing);
    126     }
    127 
    128     public NetworkStatsHistory(Parcel in) {
    129         bucketDuration = in.readLong();
    130         bucketStart = readLongArray(in);
    131         activeTime = readLongArray(in);
    132         rxBytes = readLongArray(in);
    133         rxPackets = readLongArray(in);
    134         txBytes = readLongArray(in);
    135         txPackets = readLongArray(in);
    136         operations = readLongArray(in);
    137         bucketCount = bucketStart.length;
    138         totalBytes = in.readLong();
    139     }
    140 
    141     @Override
    142     public void writeToParcel(Parcel out, int flags) {
    143         out.writeLong(bucketDuration);
    144         writeLongArray(out, bucketStart, bucketCount);
    145         writeLongArray(out, activeTime, bucketCount);
    146         writeLongArray(out, rxBytes, bucketCount);
    147         writeLongArray(out, rxPackets, bucketCount);
    148         writeLongArray(out, txBytes, bucketCount);
    149         writeLongArray(out, txPackets, bucketCount);
    150         writeLongArray(out, operations, bucketCount);
    151         out.writeLong(totalBytes);
    152     }
    153 
    154     public NetworkStatsHistory(DataInputStream in) throws IOException {
    155         final int version = in.readInt();
    156         switch (version) {
    157             case VERSION_INIT: {
    158                 bucketDuration = in.readLong();
    159                 bucketStart = readFullLongArray(in);
    160                 rxBytes = readFullLongArray(in);
    161                 rxPackets = new long[bucketStart.length];
    162                 txBytes = readFullLongArray(in);
    163                 txPackets = new long[bucketStart.length];
    164                 operations = new long[bucketStart.length];
    165                 bucketCount = bucketStart.length;
    166                 totalBytes = total(rxBytes) + total(txBytes);
    167                 break;
    168             }
    169             case VERSION_ADD_PACKETS:
    170             case VERSION_ADD_ACTIVE: {
    171                 bucketDuration = in.readLong();
    172                 bucketStart = readVarLongArray(in);
    173                 activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in)
    174                         : new long[bucketStart.length];
    175                 rxBytes = readVarLongArray(in);
    176                 rxPackets = readVarLongArray(in);
    177                 txBytes = readVarLongArray(in);
    178                 txPackets = readVarLongArray(in);
    179                 operations = readVarLongArray(in);
    180                 bucketCount = bucketStart.length;
    181                 totalBytes = total(rxBytes) + total(txBytes);
    182                 break;
    183             }
    184             default: {
    185                 throw new ProtocolException("unexpected version: " + version);
    186             }
    187         }
    188 
    189         if (bucketStart.length != bucketCount || rxBytes.length != bucketCount
    190                 || rxPackets.length != bucketCount || txBytes.length != bucketCount
    191                 || txPackets.length != bucketCount || operations.length != bucketCount) {
    192             throw new ProtocolException("Mismatched history lengths");
    193         }
    194     }
    195 
    196     public void writeToStream(DataOutputStream out) throws IOException {
    197         out.writeInt(VERSION_ADD_ACTIVE);
    198         out.writeLong(bucketDuration);
    199         writeVarLongArray(out, bucketStart, bucketCount);
    200         writeVarLongArray(out, activeTime, bucketCount);
    201         writeVarLongArray(out, rxBytes, bucketCount);
    202         writeVarLongArray(out, rxPackets, bucketCount);
    203         writeVarLongArray(out, txBytes, bucketCount);
    204         writeVarLongArray(out, txPackets, bucketCount);
    205         writeVarLongArray(out, operations, bucketCount);
    206     }
    207 
    208     @Override
    209     public int describeContents() {
    210         return 0;
    211     }
    212 
    213     public int size() {
    214         return bucketCount;
    215     }
    216 
    217     public long getBucketDuration() {
    218         return bucketDuration;
    219     }
    220 
    221     public long getStart() {
    222         if (bucketCount > 0) {
    223             return bucketStart[0];
    224         } else {
    225             return Long.MAX_VALUE;
    226         }
    227     }
    228 
    229     public long getEnd() {
    230         if (bucketCount > 0) {
    231             return bucketStart[bucketCount - 1] + bucketDuration;
    232         } else {
    233             return Long.MIN_VALUE;
    234         }
    235     }
    236 
    237     /**
    238      * Return total bytes represented by this history.
    239      */
    240     public long getTotalBytes() {
    241         return totalBytes;
    242     }
    243 
    244     /**
    245      * Return index of bucket that contains or is immediately before the
    246      * requested time.
    247      */
    248     public int getIndexBefore(long time) {
    249         int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
    250         if (index < 0) {
    251             index = (~index) - 1;
    252         } else {
    253             index -= 1;
    254         }
    255         return MathUtils.constrain(index, 0, bucketCount - 1);
    256     }
    257 
    258     /**
    259      * Return index of bucket that contains or is immediately after the
    260      * requested time.
    261      */
    262     public int getIndexAfter(long time) {
    263         int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
    264         if (index < 0) {
    265             index = ~index;
    266         } else {
    267             index += 1;
    268         }
    269         return MathUtils.constrain(index, 0, bucketCount - 1);
    270     }
    271 
    272     /**
    273      * Return specific stats entry.
    274      */
    275     public Entry getValues(int i, Entry recycle) {
    276         final Entry entry = recycle != null ? recycle : new Entry();
    277         entry.bucketStart = bucketStart[i];
    278         entry.bucketDuration = bucketDuration;
    279         entry.activeTime = getLong(activeTime, i, UNKNOWN);
    280         entry.rxBytes = getLong(rxBytes, i, UNKNOWN);
    281         entry.rxPackets = getLong(rxPackets, i, UNKNOWN);
    282         entry.txBytes = getLong(txBytes, i, UNKNOWN);
    283         entry.txPackets = getLong(txPackets, i, UNKNOWN);
    284         entry.operations = getLong(operations, i, UNKNOWN);
    285         return entry;
    286     }
    287 
    288     public void setValues(int i, Entry entry) {
    289         // Unwind old values
    290         if (rxBytes != null) totalBytes -= rxBytes[i];
    291         if (txBytes != null) totalBytes -= txBytes[i];
    292 
    293         bucketStart[i] = entry.bucketStart;
    294         setLong(activeTime, i, entry.activeTime);
    295         setLong(rxBytes, i, entry.rxBytes);
    296         setLong(rxPackets, i, entry.rxPackets);
    297         setLong(txBytes, i, entry.txBytes);
    298         setLong(txPackets, i, entry.txPackets);
    299         setLong(operations, i, entry.operations);
    300 
    301         // Apply new values
    302         if (rxBytes != null) totalBytes += rxBytes[i];
    303         if (txBytes != null) totalBytes += txBytes[i];
    304     }
    305 
    306     /**
    307      * Record that data traffic occurred in the given time range. Will
    308      * distribute across internal buckets, creating new buckets as needed.
    309      */
    310     @Deprecated
    311     public void recordData(long start, long end, long rxBytes, long txBytes) {
    312         recordData(start, end, new NetworkStats.Entry(
    313                 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
    314     }
    315 
    316     /**
    317      * Record that data traffic occurred in the given time range. Will
    318      * distribute across internal buckets, creating new buckets as needed.
    319      */
    320     public void recordData(long start, long end, NetworkStats.Entry entry) {
    321         long rxBytes = entry.rxBytes;
    322         long rxPackets = entry.rxPackets;
    323         long txBytes = entry.txBytes;
    324         long txPackets = entry.txPackets;
    325         long operations = entry.operations;
    326 
    327         if (entry.isNegative()) {
    328             throw new IllegalArgumentException("tried recording negative data");
    329         }
    330         if (entry.isEmpty()) {
    331             return;
    332         }
    333 
    334         // create any buckets needed by this range
    335         ensureBuckets(start, end);
    336 
    337         // distribute data usage into buckets
    338         long duration = end - start;
    339         final int startIndex = getIndexAfter(end);
    340         for (int i = startIndex; i >= 0; i--) {
    341             final long curStart = bucketStart[i];
    342             final long curEnd = curStart + bucketDuration;
    343 
    344             // bucket is older than record; we're finished
    345             if (curEnd < start) break;
    346             // bucket is newer than record; keep looking
    347             if (curStart > end) continue;
    348 
    349             final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
    350             if (overlap <= 0) continue;
    351 
    352             // integer math each time is faster than floating point
    353             final long fracRxBytes = rxBytes * overlap / duration;
    354             final long fracRxPackets = rxPackets * overlap / duration;
    355             final long fracTxBytes = txBytes * overlap / duration;
    356             final long fracTxPackets = txPackets * overlap / duration;
    357             final long fracOperations = operations * overlap / duration;
    358 
    359             addLong(activeTime, i, overlap);
    360             addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes;
    361             addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets;
    362             addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes;
    363             addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets;
    364             addLong(this.operations, i, fracOperations); operations -= fracOperations;
    365 
    366             duration -= overlap;
    367         }
    368 
    369         totalBytes += entry.rxBytes + entry.txBytes;
    370     }
    371 
    372     /**
    373      * Record an entire {@link NetworkStatsHistory} into this history. Usually
    374      * for combining together stats for external reporting.
    375      */
    376     public void recordEntireHistory(NetworkStatsHistory input) {
    377         recordHistory(input, Long.MIN_VALUE, Long.MAX_VALUE);
    378     }
    379 
    380     /**
    381      * Record given {@link NetworkStatsHistory} into this history, copying only
    382      * buckets that atomically occur in the inclusive time range. Doesn't
    383      * interpolate across partial buckets.
    384      */
    385     public void recordHistory(NetworkStatsHistory input, long start, long end) {
    386         final NetworkStats.Entry entry = new NetworkStats.Entry(
    387                 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
    388         for (int i = 0; i < input.bucketCount; i++) {
    389             final long bucketStart = input.bucketStart[i];
    390             final long bucketEnd = bucketStart + input.bucketDuration;
    391 
    392             // skip when bucket is outside requested range
    393             if (bucketStart < start || bucketEnd > end) continue;
    394 
    395             entry.rxBytes = getLong(input.rxBytes, i, 0L);
    396             entry.rxPackets = getLong(input.rxPackets, i, 0L);
    397             entry.txBytes = getLong(input.txBytes, i, 0L);
    398             entry.txPackets = getLong(input.txPackets, i, 0L);
    399             entry.operations = getLong(input.operations, i, 0L);
    400 
    401             recordData(bucketStart, bucketEnd, entry);
    402         }
    403     }
    404 
    405     /**
    406      * Ensure that buckets exist for given time range, creating as needed.
    407      */
    408     private void ensureBuckets(long start, long end) {
    409         // normalize incoming range to bucket boundaries
    410         start -= start % bucketDuration;
    411         end += (bucketDuration - (end % bucketDuration)) % bucketDuration;
    412 
    413         for (long now = start; now < end; now += bucketDuration) {
    414             // try finding existing bucket
    415             final int index = Arrays.binarySearch(bucketStart, 0, bucketCount, now);
    416             if (index < 0) {
    417                 // bucket missing, create and insert
    418                 insertBucket(~index, now);
    419             }
    420         }
    421     }
    422 
    423     /**
    424      * Insert new bucket at requested index and starting time.
    425      */
    426     private void insertBucket(int index, long start) {
    427         // create more buckets when needed
    428         if (bucketCount >= bucketStart.length) {
    429             final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
    430             bucketStart = Arrays.copyOf(bucketStart, newLength);
    431             if (activeTime != null) activeTime = Arrays.copyOf(activeTime, newLength);
    432             if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength);
    433             if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength);
    434             if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength);
    435             if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength);
    436             if (operations != null) operations = Arrays.copyOf(operations, newLength);
    437         }
    438 
    439         // create gap when inserting bucket in middle
    440         if (index < bucketCount) {
    441             final int dstPos = index + 1;
    442             final int length = bucketCount - index;
    443 
    444             System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
    445             if (activeTime != null) System.arraycopy(activeTime, index, activeTime, dstPos, length);
    446             if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
    447             if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
    448             if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length);
    449             if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length);
    450             if (operations != null) System.arraycopy(operations, index, operations, dstPos, length);
    451         }
    452 
    453         bucketStart[index] = start;
    454         setLong(activeTime, index, 0L);
    455         setLong(rxBytes, index, 0L);
    456         setLong(rxPackets, index, 0L);
    457         setLong(txBytes, index, 0L);
    458         setLong(txPackets, index, 0L);
    459         setLong(operations, index, 0L);
    460         bucketCount++;
    461     }
    462 
    463     /**
    464      * Clear all data stored in this object.
    465      */
    466     public void clear() {
    467         bucketStart = EmptyArray.LONG;
    468         if (activeTime != null) activeTime = EmptyArray.LONG;
    469         if (rxBytes != null) rxBytes = EmptyArray.LONG;
    470         if (rxPackets != null) rxPackets = EmptyArray.LONG;
    471         if (txBytes != null) txBytes = EmptyArray.LONG;
    472         if (txPackets != null) txPackets = EmptyArray.LONG;
    473         if (operations != null) operations = EmptyArray.LONG;
    474         bucketCount = 0;
    475         totalBytes = 0;
    476     }
    477 
    478     /**
    479      * Remove buckets older than requested cutoff.
    480      */
    481     @Deprecated
    482     public void removeBucketsBefore(long cutoff) {
    483         int i;
    484         for (i = 0; i < bucketCount; i++) {
    485             final long curStart = bucketStart[i];
    486             final long curEnd = curStart + bucketDuration;
    487 
    488             // cutoff happens before or during this bucket; everything before
    489             // this bucket should be removed.
    490             if (curEnd > cutoff) break;
    491         }
    492 
    493         if (i > 0) {
    494             final int length = bucketStart.length;
    495             bucketStart = Arrays.copyOfRange(bucketStart, i, length);
    496             if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, i, length);
    497             if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length);
    498             if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length);
    499             if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length);
    500             if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
    501             if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
    502             bucketCount -= i;
    503 
    504             // TODO: subtract removed values from totalBytes
    505         }
    506     }
    507 
    508     /**
    509      * Return interpolated data usage across the requested range. Interpolates
    510      * across buckets, so values may be rounded slightly.
    511      */
    512     public Entry getValues(long start, long end, Entry recycle) {
    513         return getValues(start, end, Long.MAX_VALUE, recycle);
    514     }
    515 
    516     /**
    517      * Return interpolated data usage across the requested range. Interpolates
    518      * across buckets, so values may be rounded slightly.
    519      */
    520     public Entry getValues(long start, long end, long now, Entry recycle) {
    521         final Entry entry = recycle != null ? recycle : new Entry();
    522         entry.bucketDuration = end - start;
    523         entry.bucketStart = start;
    524         entry.activeTime = activeTime != null ? 0 : UNKNOWN;
    525         entry.rxBytes = rxBytes != null ? 0 : UNKNOWN;
    526         entry.rxPackets = rxPackets != null ? 0 : UNKNOWN;
    527         entry.txBytes = txBytes != null ? 0 : UNKNOWN;
    528         entry.txPackets = txPackets != null ? 0 : UNKNOWN;
    529         entry.operations = operations != null ? 0 : UNKNOWN;
    530 
    531         final int startIndex = getIndexAfter(end);
    532         for (int i = startIndex; i >= 0; i--) {
    533             final long curStart = bucketStart[i];
    534             final long curEnd = curStart + bucketDuration;
    535 
    536             // bucket is older than request; we're finished
    537             if (curEnd <= start) break;
    538             // bucket is newer than request; keep looking
    539             if (curStart >= end) continue;
    540 
    541             // include full value for active buckets, otherwise only fractional
    542             final boolean activeBucket = curStart < now && curEnd > now;
    543             final long overlap;
    544             if (activeBucket) {
    545                 overlap = bucketDuration;
    546             } else {
    547                 final long overlapEnd = curEnd < end ? curEnd : end;
    548                 final long overlapStart = curStart > start ? curStart : start;
    549                 overlap = overlapEnd - overlapStart;
    550             }
    551             if (overlap <= 0) continue;
    552 
    553             // integer math each time is faster than floating point
    554             if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketDuration;
    555             if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
    556             if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
    557             if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration;
    558             if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
    559             if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
    560         }
    561         return entry;
    562     }
    563 
    564     /**
    565      * @deprecated only for temporary testing
    566      */
    567     @Deprecated
    568     public void generateRandom(long start, long end, long bytes) {
    569         final Random r = new Random();
    570 
    571         final float fractionRx = r.nextFloat();
    572         final long rxBytes = (long) (bytes * fractionRx);
    573         final long txBytes = (long) (bytes * (1 - fractionRx));
    574 
    575         final long rxPackets = rxBytes / 1024;
    576         final long txPackets = txBytes / 1024;
    577         final long operations = rxBytes / 2048;
    578 
    579         generateRandom(start, end, rxBytes, rxPackets, txBytes, txPackets, operations, r);
    580     }
    581 
    582     /**
    583      * @deprecated only for temporary testing
    584      */
    585     @Deprecated
    586     public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
    587             long txPackets, long operations, Random r) {
    588         ensureBuckets(start, end);
    589 
    590         final NetworkStats.Entry entry = new NetworkStats.Entry(
    591                 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
    592         while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128
    593                 || operations > 32) {
    594             final long curStart = randomLong(r, start, end);
    595             final long curEnd = curStart + randomLong(r, 0, (end - curStart) / 2);
    596 
    597             entry.rxBytes = randomLong(r, 0, rxBytes);
    598             entry.rxPackets = randomLong(r, 0, rxPackets);
    599             entry.txBytes = randomLong(r, 0, txBytes);
    600             entry.txPackets = randomLong(r, 0, txPackets);
    601             entry.operations = randomLong(r, 0, operations);
    602 
    603             rxBytes -= entry.rxBytes;
    604             rxPackets -= entry.rxPackets;
    605             txBytes -= entry.txBytes;
    606             txPackets -= entry.txPackets;
    607             operations -= entry.operations;
    608 
    609             recordData(curStart, curEnd, entry);
    610         }
    611     }
    612 
    613     public static long randomLong(Random r, long start, long end) {
    614         return (long) (start + (r.nextFloat() * (end - start)));
    615     }
    616 
    617     /**
    618      * Quickly determine if this history intersects with given window.
    619      */
    620     public boolean intersects(long start, long end) {
    621         final long dataStart = getStart();
    622         final long dataEnd = getEnd();
    623         if (start >= dataStart && start <= dataEnd) return true;
    624         if (end >= dataStart && end <= dataEnd) return true;
    625         if (dataStart >= start && dataStart <= end) return true;
    626         if (dataEnd >= start && dataEnd <= end) return true;
    627         return false;
    628     }
    629 
    630     public void dump(IndentingPrintWriter pw, boolean fullHistory) {
    631         pw.print("NetworkStatsHistory: bucketDuration=");
    632         pw.println(bucketDuration / SECOND_IN_MILLIS);
    633         pw.increaseIndent();
    634 
    635         final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
    636         if (start > 0) {
    637             pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
    638         }
    639 
    640         for (int i = start; i < bucketCount; i++) {
    641             pw.print("st="); pw.print(bucketStart[i] / SECOND_IN_MILLIS);
    642             if (rxBytes != null) { pw.print(" rb="); pw.print(rxBytes[i]); }
    643             if (rxPackets != null) { pw.print(" rp="); pw.print(rxPackets[i]); }
    644             if (txBytes != null) { pw.print(" tb="); pw.print(txBytes[i]); }
    645             if (txPackets != null) { pw.print(" tp="); pw.print(txPackets[i]); }
    646             if (operations != null) { pw.print(" op="); pw.print(operations[i]); }
    647             pw.println();
    648         }
    649 
    650         pw.decreaseIndent();
    651     }
    652 
    653     public void dumpCheckin(PrintWriter pw) {
    654         pw.print("d,");
    655         pw.print(bucketDuration / SECOND_IN_MILLIS);
    656         pw.println();
    657 
    658         for (int i = 0; i < bucketCount; i++) {
    659             pw.print("b,");
    660             pw.print(bucketStart[i] / SECOND_IN_MILLIS); pw.print(',');
    661             if (rxBytes != null) { pw.print(rxBytes[i]); } else { pw.print("*"); } pw.print(',');
    662             if (rxPackets != null) { pw.print(rxPackets[i]); } else { pw.print("*"); } pw.print(',');
    663             if (txBytes != null) { pw.print(txBytes[i]); } else { pw.print("*"); } pw.print(',');
    664             if (txPackets != null) { pw.print(txPackets[i]); } else { pw.print("*"); } pw.print(',');
    665             if (operations != null) { pw.print(operations[i]); } else { pw.print("*"); }
    666             pw.println();
    667         }
    668     }
    669 
    670     public void writeToProto(ProtoOutputStream proto, long tag) {
    671         final long start = proto.start(tag);
    672 
    673         proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
    674 
    675         for (int i = 0; i < bucketCount; i++) {
    676             final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
    677 
    678             proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
    679             writeToProto(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
    680             writeToProto(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
    681             writeToProto(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
    682             writeToProto(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
    683             writeToProto(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
    684 
    685             proto.end(startBucket);
    686         }
    687 
    688         proto.end(start);
    689     }
    690 
    691     private static void writeToProto(ProtoOutputStream proto, long tag, long[] array, int index) {
    692         if (array != null) {
    693             proto.write(tag, array[index]);
    694         }
    695     }
    696 
    697     @Override
    698     public String toString() {
    699         final CharArrayWriter writer = new CharArrayWriter();
    700         dump(new IndentingPrintWriter(writer, "  "), false);
    701         return writer.toString();
    702     }
    703 
    704     public static final Creator<NetworkStatsHistory> CREATOR = new Creator<NetworkStatsHistory>() {
    705         @Override
    706         public NetworkStatsHistory createFromParcel(Parcel in) {
    707             return new NetworkStatsHistory(in);
    708         }
    709 
    710         @Override
    711         public NetworkStatsHistory[] newArray(int size) {
    712             return new NetworkStatsHistory[size];
    713         }
    714     };
    715 
    716     private static long getLong(long[] array, int i, long value) {
    717         return array != null ? array[i] : value;
    718     }
    719 
    720     private static void setLong(long[] array, int i, long value) {
    721         if (array != null) array[i] = value;
    722     }
    723 
    724     private static void addLong(long[] array, int i, long value) {
    725         if (array != null) array[i] += value;
    726     }
    727 
    728     public int estimateResizeBuckets(long newBucketDuration) {
    729         return (int) (size() * getBucketDuration() / newBucketDuration);
    730     }
    731 
    732     /**
    733      * Utility methods for interacting with {@link DataInputStream} and
    734      * {@link DataOutputStream}, mostly dealing with writing partial arrays.
    735      */
    736     public static class DataStreamUtils {
    737         @Deprecated
    738         public static long[] readFullLongArray(DataInputStream in) throws IOException {
    739             final int size = in.readInt();
    740             if (size < 0) throw new ProtocolException("negative array size");
    741             final long[] values = new long[size];
    742             for (int i = 0; i < values.length; i++) {
    743                 values[i] = in.readLong();
    744             }
    745             return values;
    746         }
    747 
    748         /**
    749          * Read variable-length {@link Long} using protobuf-style approach.
    750          */
    751         public static long readVarLong(DataInputStream in) throws IOException {
    752             int shift = 0;
    753             long result = 0;
    754             while (shift < 64) {
    755                 byte b = in.readByte();
    756                 result |= (long) (b & 0x7F) << shift;
    757                 if ((b & 0x80) == 0)
    758                     return result;
    759                 shift += 7;
    760             }
    761             throw new ProtocolException("malformed long");
    762         }
    763 
    764         /**
    765          * Write variable-length {@link Long} using protobuf-style approach.
    766          */
    767         public static void writeVarLong(DataOutputStream out, long value) throws IOException {
    768             while (true) {
    769                 if ((value & ~0x7FL) == 0) {
    770                     out.writeByte((int) value);
    771                     return;
    772                 } else {
    773                     out.writeByte(((int) value & 0x7F) | 0x80);
    774                     value >>>= 7;
    775                 }
    776             }
    777         }
    778 
    779         public static long[] readVarLongArray(DataInputStream in) throws IOException {
    780             final int size = in.readInt();
    781             if (size == -1) return null;
    782             if (size < 0) throw new ProtocolException("negative array size");
    783             final long[] values = new long[size];
    784             for (int i = 0; i < values.length; i++) {
    785                 values[i] = readVarLong(in);
    786             }
    787             return values;
    788         }
    789 
    790         public static void writeVarLongArray(DataOutputStream out, long[] values, int size)
    791                 throws IOException {
    792             if (values == null) {
    793                 out.writeInt(-1);
    794                 return;
    795             }
    796             if (size > values.length) {
    797                 throw new IllegalArgumentException("size larger than length");
    798             }
    799             out.writeInt(size);
    800             for (int i = 0; i < size; i++) {
    801                 writeVarLong(out, values[i]);
    802             }
    803         }
    804     }
    805 
    806     /**
    807      * Utility methods for interacting with {@link Parcel} structures, mostly
    808      * dealing with writing partial arrays.
    809      */
    810     public static class ParcelUtils {
    811         public static long[] readLongArray(Parcel in) {
    812             final int size = in.readInt();
    813             if (size == -1) return null;
    814             final long[] values = new long[size];
    815             for (int i = 0; i < values.length; i++) {
    816                 values[i] = in.readLong();
    817             }
    818             return values;
    819         }
    820 
    821         public static void writeLongArray(Parcel out, long[] values, int size) {
    822             if (values == null) {
    823                 out.writeInt(-1);
    824                 return;
    825             }
    826             if (size > values.length) {
    827                 throw new IllegalArgumentException("size larger than length");
    828             }
    829             out.writeInt(size);
    830             for (int i = 0; i < size; i++) {
    831                 out.writeLong(values[i]);
    832             }
    833         }
    834     }
    835 
    836 }
    837