| Index: third_party/android_snippets/java/src/org/chromium/third_party/android/datausagechart/NetworkStatsHistory.java | 
| diff --git a/third_party/android_snippets/java/src/org/chromium/third_party/android/datausagechart/NetworkStatsHistory.java b/third_party/android_snippets/java/src/org/chromium/third_party/android/datausagechart/NetworkStatsHistory.java | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..01f98e912edd703fc405400faafe138da0c0d8ef | 
| --- /dev/null | 
| +++ b/third_party/android_snippets/java/src/org/chromium/third_party/android/datausagechart/NetworkStatsHistory.java | 
| @@ -0,0 +1,737 @@ | 
| +/* | 
| + * Copyright (C) 2011 The Android Open Source Project | 
| + * | 
| + * Licensed under the Apache License, Version 2.0 (the "License"); | 
| + * you may not use this file except in compliance with the License. | 
| + * You may obtain a copy of the License at | 
| + * | 
| + *      http://www.apache.org/licenses/LICENSE-2.0 | 
| + * | 
| + * Unless required by applicable law or agreed to in writing, software | 
| + * distributed under the License is distributed on an "AS IS" BASIS, | 
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| + * See the License for the specific language governing permissions and | 
| + * limitations under the License. | 
| + */ | 
| + | 
| +package org.chromium.third_party.android.datausagechart; | 
| + | 
| +import static org.chromium.third_party.android.datausagechart.NetworkStatsHistory.DataStreamUtils.readFullLongArray; | 
| +import static org.chromium.third_party.android.datausagechart.NetworkStatsHistory.DataStreamUtils.readVarLongArray; | 
| +import static org.chromium.third_party.android.datausagechart.NetworkStatsHistory.DataStreamUtils.writeVarLongArray; | 
| +import static org.chromium.third_party.android.datausagechart.NetworkStatsHistory.Entry.UNKNOWN; | 
| +import static org.chromium.third_party.android.datausagechart.NetworkStatsHistory.ParcelUtils.readLongArray; | 
| +import static org.chromium.third_party.android.datausagechart.NetworkStatsHistory.ParcelUtils.writeLongArray; | 
| + | 
| +import android.os.Parcel; | 
| +import android.os.Parcelable; | 
| + | 
| +import java.io.CharArrayWriter; | 
| +import java.io.DataInputStream; | 
| +import java.io.DataOutputStream; | 
| +import java.io.IOException; | 
| +import java.io.PrintWriter; | 
| +import java.net.ProtocolException; | 
| +import java.util.Arrays; | 
| +import java.util.Random; | 
| + | 
| +/** | 
| + * Collection of historical network statistics, recorded into equally-sized | 
| + * "buckets" in time. Internally it stores data in {@code long} series for more | 
| + * efficient persistence. | 
| + * <p> | 
| + * Each bucket is defined by a {@link #bucketStart} timestamp, and lasts for | 
| + * {@link #bucketDuration}. Internally assumes that {@link #bucketStart} is | 
| + * sorted at all times. | 
| + * This is derived from android.net.NetworkStatsHistory. | 
| + * @hide | 
| + */ | 
| +public class NetworkStatsHistory implements Parcelable { | 
| +    private static final int VERSION_INIT = 1; | 
| +    private static final int VERSION_ADD_PACKETS = 2; | 
| +    private static final int VERSION_ADD_ACTIVE = 3; | 
| + | 
| +    public static final int FIELD_ACTIVE_TIME = 0x01; | 
| +    public static final int FIELD_RX_BYTES = 0x02; | 
| +    public static final int FIELD_RX_PACKETS = 0x04; | 
| +    public static final int FIELD_TX_BYTES = 0x08; | 
| +    public static final int FIELD_TX_PACKETS = 0x10; | 
| +    public static final int FIELD_OPERATIONS = 0x20; | 
| + | 
| +    public static final int FIELD_ALL = 0xFFFFFFFF; | 
| + | 
| +    public static final String IFACE_ALL = null; | 
| +    public static final int SET_DEFAULT = 0; | 
| +    public static final int TAG_NONE = 0; | 
| +    public static final int UID_ALL = -1; | 
| + | 
| +    private long bucketDuration; | 
| +    private int bucketCount; | 
| +    private long[] bucketStart; | 
| +    private long[] activeTime; | 
| +    private long[] rxBytes; | 
| +    private long[] rxPackets; | 
| +    private long[] txBytes; | 
| +    private long[] txPackets; | 
| +    private long[] operations; | 
| +    private long totalBytes; | 
| + | 
| +    public static class Entry { | 
| +        public static final long UNKNOWN = -1; | 
| + | 
| +        public long bucketDuration; | 
| +        public long bucketStart; | 
| +        public long activeTime; | 
| +        public long rxBytes; | 
| +        public long rxPackets; | 
| +        public long txBytes; | 
| +        public long txPackets; | 
| +        public long operations; | 
| +    } | 
| + | 
| +    public NetworkStatsHistory(long bucketDuration) { | 
| +        this(bucketDuration, 10, FIELD_ALL); | 
| +    } | 
| + | 
| +    public NetworkStatsHistory(long bucketDuration, int initialSize) { | 
| +        this(bucketDuration, initialSize, FIELD_ALL); | 
| +    } | 
| + | 
| +    public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) { | 
| +        this.bucketDuration = bucketDuration; | 
| +        bucketStart = new long[initialSize]; | 
| +        if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[initialSize]; | 
| +        if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize]; | 
| +        if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize]; | 
| +        if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize]; | 
| +        if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize]; | 
| +        if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize]; | 
| +        bucketCount = 0; | 
| +        totalBytes = 0; | 
| +    } | 
| + | 
| +    public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) { | 
| +        this(bucketDuration, existing.estimateResizeBuckets(bucketDuration)); | 
| +        recordEntireHistory(existing); | 
| +    } | 
| + | 
| +    public NetworkStatsHistory(Parcel in) { | 
| +        bucketDuration = in.readLong(); | 
| +        bucketStart = readLongArray(in); | 
| +        activeTime = readLongArray(in); | 
| +        rxBytes = readLongArray(in); | 
| +        rxPackets = readLongArray(in); | 
| +        txBytes = readLongArray(in); | 
| +        txPackets = readLongArray(in); | 
| +        operations = readLongArray(in); | 
| +        bucketCount = bucketStart.length; | 
| +        totalBytes = in.readLong(); | 
| +    } | 
| + | 
| +    @Override | 
| +    public void writeToParcel(Parcel out, int flags) { | 
| +        out.writeLong(bucketDuration); | 
| +        writeLongArray(out, bucketStart, bucketCount); | 
| +        writeLongArray(out, activeTime, bucketCount); | 
| +        writeLongArray(out, rxBytes, bucketCount); | 
| +        writeLongArray(out, rxPackets, bucketCount); | 
| +        writeLongArray(out, txBytes, bucketCount); | 
| +        writeLongArray(out, txPackets, bucketCount); | 
| +        writeLongArray(out, operations, bucketCount); | 
| +        out.writeLong(totalBytes); | 
| +    } | 
| + | 
| +    private long total(long[] list) { | 
| +        long sum = 0L; | 
| +        for (int i = 0; i < list.length; i++) { | 
| +            sum += list[i]; | 
| +        } | 
| +        return sum; | 
| +    } | 
| + | 
| +    public NetworkStatsHistory(DataInputStream in) throws IOException { | 
| +        final int version = in.readInt(); | 
| +        switch (version) { | 
| +            case VERSION_INIT: { | 
| +                bucketDuration = in.readLong(); | 
| +                bucketStart = readFullLongArray(in); | 
| +                rxBytes = readFullLongArray(in); | 
| +                rxPackets = new long[bucketStart.length]; | 
| +                txBytes = readFullLongArray(in); | 
| +                txPackets = new long[bucketStart.length]; | 
| +                operations = new long[bucketStart.length]; | 
| +                bucketCount = bucketStart.length; | 
| +                totalBytes = total(rxBytes) + total(txBytes); | 
| +                break; | 
| +            } | 
| +            case VERSION_ADD_PACKETS: | 
| +            case VERSION_ADD_ACTIVE: { | 
| +                bucketDuration = in.readLong(); | 
| +                bucketStart = readVarLongArray(in); | 
| +                activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in) | 
| +                        : new long[bucketStart.length]; | 
| +                rxBytes = readVarLongArray(in); | 
| +                rxPackets = readVarLongArray(in); | 
| +                txBytes = readVarLongArray(in); | 
| +                txPackets = readVarLongArray(in); | 
| +                operations = readVarLongArray(in); | 
| +                bucketCount = bucketStart.length; | 
| +                totalBytes = total(rxBytes) + total(txBytes); | 
| +                break; | 
| +            } | 
| +            default: { | 
| +                throw new ProtocolException("unexpected version: " + version); | 
| +            } | 
| +        } | 
| + | 
| +        if (bucketStart.length != bucketCount || rxBytes.length != bucketCount | 
| +                || rxPackets.length != bucketCount || txBytes.length != bucketCount | 
| +                || txPackets.length != bucketCount || operations.length != bucketCount) { | 
| +            throw new ProtocolException("Mismatched history lengths"); | 
| +        } | 
| +    } | 
| + | 
| +    public void writeToStream(DataOutputStream out) throws IOException { | 
| +        out.writeInt(VERSION_ADD_ACTIVE); | 
| +        out.writeLong(bucketDuration); | 
| +        writeVarLongArray(out, bucketStart, bucketCount); | 
| +        writeVarLongArray(out, activeTime, bucketCount); | 
| +        writeVarLongArray(out, rxBytes, bucketCount); | 
| +        writeVarLongArray(out, rxPackets, bucketCount); | 
| +        writeVarLongArray(out, txBytes, bucketCount); | 
| +        writeVarLongArray(out, txPackets, bucketCount); | 
| +        writeVarLongArray(out, operations, bucketCount); | 
| +    } | 
| + | 
| +    @Override | 
| +    public int describeContents() { | 
| +        return 0; | 
| +    } | 
| + | 
| +    public int size() { | 
| +        return bucketCount; | 
| +    } | 
| + | 
| +    public long getBucketDuration() { | 
| +        return bucketDuration; | 
| +    } | 
| + | 
| +    public long getStart() { | 
| +        if (bucketCount > 0) { | 
| +            return bucketStart[0]; | 
| +        } else { | 
| +            return Long.MAX_VALUE; | 
| +        } | 
| +    } | 
| + | 
| +    public long getEnd() { | 
| +        if (bucketCount > 0) { | 
| +            return bucketStart[bucketCount - 1] + bucketDuration; | 
| +        } else { | 
| +            return Long.MIN_VALUE; | 
| +        } | 
| +    } | 
| + | 
| +    /** | 
| +     * Return total bytes represented by this history. | 
| +     */ | 
| +    public long getTotalBytes() { | 
| +        return totalBytes; | 
| +    } | 
| + | 
| +    /** | 
| +     * Return index of bucket that contains or is immediately before the | 
| +     * requested time. | 
| +     */ | 
| +    public int getIndexBefore(long time) { | 
| +        int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time); | 
| +        if (index < 0) { | 
| +            index = (~index) - 1; | 
| +        } else { | 
| +            index -= 1; | 
| +        } | 
| +        return Math.max(0, Math.min(bucketCount - 1, index)); | 
| +    } | 
| + | 
| +    /** | 
| +     * Return index of bucket that contains or is immediately after the | 
| +     * requested time. | 
| +     */ | 
| +    public int getIndexAfter(long time) { | 
| +        int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time); | 
| +        if (index < 0) { | 
| +            index = ~index; | 
| +        } else { | 
| +            index += 1; | 
| +        } | 
| +        return Math.max(0, Math.min(bucketCount - 1, index)); | 
| +    } | 
| + | 
| +    /** | 
| +     * Return specific stats entry. | 
| +     */ | 
| +    public Entry getValues(int i, Entry recycle) { | 
| +        final Entry entry = recycle != null ? recycle : new Entry(); | 
| +        entry.bucketStart = bucketStart[i]; | 
| +        entry.bucketDuration = bucketDuration; | 
| +        entry.activeTime = getLong(activeTime, i, UNKNOWN); | 
| +        entry.rxBytes = getLong(rxBytes, i, UNKNOWN); | 
| +        entry.rxPackets = getLong(rxPackets, i, UNKNOWN); | 
| +        entry.txBytes = getLong(txBytes, i, UNKNOWN); | 
| +        entry.txPackets = getLong(txPackets, i, UNKNOWN); | 
| +        entry.operations = getLong(operations, i, UNKNOWN); | 
| +        return entry; | 
| +    } | 
| + | 
| +    /** | 
| +     * Record that data traffic occurred in the given time range. Will | 
| +     * distribute across internal buckets, creating new buckets as needed. | 
| +     */ | 
| +    @Deprecated | 
| +    public void recordData(long start, long end, long rxBytes, long txBytes) { | 
| +        recordData(start, end, new NetworkStats.Entry( | 
| +                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L)); | 
| +    } | 
| + | 
| +    /** | 
| +     * Record that data traffic occurred in the given time range. Will | 
| +     * distribute across internal buckets, creating new buckets as needed. | 
| +     */ | 
| +    public void recordData(long start, long end, NetworkStats.Entry entry) { | 
| +        long rxBytes = entry.rxBytes; | 
| +        long rxPackets = entry.rxPackets; | 
| +        long txBytes = entry.txBytes; | 
| +        long txPackets = entry.txPackets; | 
| +        long operations = entry.operations; | 
| + | 
| +        if (entry.isNegative()) { | 
| +            throw new IllegalArgumentException("tried recording negative data"); | 
| +        } | 
| + | 
| +        // create any buckets needed by this range | 
| +        ensureBuckets(start, end); | 
| + | 
| +        // distribute data usage into buckets | 
| +        long duration = end - start; | 
| +        final int startIndex = getIndexAfter(end); | 
| +        for (int i = startIndex; i >= 0; i--) { | 
| +            final long curStart = bucketStart[i]; | 
| +            final long curEnd = curStart + bucketDuration; | 
| + | 
| +            // bucket is older than record; we're finished | 
| +            if (curEnd < start) break; | 
| +            // bucket is newer than record; keep looking | 
| +            if (curStart > end) continue; | 
| + | 
| +            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); | 
| +            if (overlap <= 0) continue; | 
| + | 
| +            // integer math each time is faster than floating point | 
| +            final long fracRxBytes = rxBytes * overlap / duration; | 
| +            final long fracRxPackets = rxPackets * overlap / duration; | 
| +            final long fracTxBytes = txBytes * overlap / duration; | 
| +            final long fracTxPackets = txPackets * overlap / duration; | 
| +            final long fracOperations = operations * overlap / duration; | 
| + | 
| +            addLong(activeTime, i, overlap); | 
| +            addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes; | 
| +            addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets; | 
| +            addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes; | 
| +            addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets; | 
| +            addLong(this.operations, i, fracOperations); operations -= fracOperations; | 
| + | 
| +            duration -= overlap; | 
| +        } | 
| + | 
| +        totalBytes += entry.rxBytes + entry.txBytes; | 
| +    } | 
| + | 
| +    /** | 
| +     * Record an entire {@link NetworkStatsHistory} into this history. Usually | 
| +     * for combining together stats for external reporting. | 
| +     */ | 
| +    public void recordEntireHistory(NetworkStatsHistory input) { | 
| +        recordHistory(input, Long.MIN_VALUE, Long.MAX_VALUE); | 
| +    } | 
| + | 
| +    /** | 
| +     * Record given {@link NetworkStatsHistory} into this history, copying only | 
| +     * buckets that atomically occur in the inclusive time range. Doesn't | 
| +     * interpolate across partial buckets. | 
| +     */ | 
| +    public void recordHistory(NetworkStatsHistory input, long start, long end) { | 
| +        final NetworkStats.Entry entry = new NetworkStats.Entry( | 
| +                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L); | 
| +        for (int i = 0; i < input.bucketCount; i++) { | 
| +            final long bucketStart = input.bucketStart[i]; | 
| +            final long bucketEnd = bucketStart + input.bucketDuration; | 
| + | 
| +            // skip when bucket is outside requested range | 
| +            if (bucketStart < start || bucketEnd > end) continue; | 
| + | 
| +            entry.rxBytes = getLong(input.rxBytes, i, 0L); | 
| +            entry.rxPackets = getLong(input.rxPackets, i, 0L); | 
| +            entry.txBytes = getLong(input.txBytes, i, 0L); | 
| +            entry.txPackets = getLong(input.txPackets, i, 0L); | 
| +            entry.operations = getLong(input.operations, i, 0L); | 
| + | 
| +            recordData(bucketStart, bucketEnd, entry); | 
| +        } | 
| +    } | 
| + | 
| +    /** | 
| +     * Ensure that buckets exist for given time range, creating as needed. | 
| +     */ | 
| +    private void ensureBuckets(long start, long end) { | 
| +        // normalize incoming range to bucket boundaries | 
| +        start -= start % bucketDuration; | 
| +        end += (bucketDuration - (end % bucketDuration)) % bucketDuration; | 
| + | 
| +        for (long now = start; now < end; now += bucketDuration) { | 
| +            // try finding existing bucket | 
| +            final int index = Arrays.binarySearch(bucketStart, 0, bucketCount, now); | 
| +            if (index < 0) { | 
| +                // bucket missing, create and insert | 
| +                insertBucket(~index, now); | 
| +            } | 
| +        } | 
| +    } | 
| + | 
| +    /** | 
| +     * Insert new bucket at requested index and starting time. | 
| +     */ | 
| +    private void insertBucket(int index, long start) { | 
| +        // create more buckets when needed | 
| +        if (bucketCount >= bucketStart.length) { | 
| +            final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; | 
| +            bucketStart = Arrays.copyOf(bucketStart, newLength); | 
| +            if (activeTime != null) activeTime = Arrays.copyOf(activeTime, newLength); | 
| +            if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength); | 
| +            if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength); | 
| +            if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength); | 
| +            if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength); | 
| +            if (operations != null) operations = Arrays.copyOf(operations, newLength); | 
| +        } | 
| + | 
| +        // create gap when inserting bucket in middle | 
| +        if (index < bucketCount) { | 
| +            final int dstPos = index + 1; | 
| +            final int length = bucketCount - index; | 
| + | 
| +            System.arraycopy(bucketStart, index, bucketStart, dstPos, length); | 
| +            if (activeTime != null) System.arraycopy(activeTime, index, activeTime, dstPos, length); | 
| +            if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length); | 
| +            if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length); | 
| +            if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length); | 
| +            if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length); | 
| +            if (operations != null) System.arraycopy(operations, index, operations, dstPos, length); | 
| +        } | 
| + | 
| +        bucketStart[index] = start; | 
| +        setLong(activeTime, index, 0L); | 
| +        setLong(rxBytes, index, 0L); | 
| +        setLong(rxPackets, index, 0L); | 
| +        setLong(txBytes, index, 0L); | 
| +        setLong(txPackets, index, 0L); | 
| +        setLong(operations, index, 0L); | 
| +        bucketCount++; | 
| +    } | 
| + | 
| +    /** | 
| +     * Remove buckets older than requested cutoff. | 
| +     */ | 
| +    @Deprecated | 
| +    public void removeBucketsBefore(long cutoff) { | 
| +        int i; | 
| +        for (i = 0; i < bucketCount; i++) { | 
| +            final long curStart = bucketStart[i]; | 
| +            final long curEnd = curStart + bucketDuration; | 
| + | 
| +            // cutoff happens before or during this bucket; everything before | 
| +            // this bucket should be removed. | 
| +            if (curEnd > cutoff) break; | 
| +        } | 
| + | 
| +        if (i > 0) { | 
| +            final int length = bucketStart.length; | 
| +            bucketStart = Arrays.copyOfRange(bucketStart, i, length); | 
| +            if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, i, length); | 
| +            if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length); | 
| +            if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length); | 
| +            if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length); | 
| +            if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length); | 
| +            if (operations != null) operations = Arrays.copyOfRange(operations, i, length); | 
| +            bucketCount -= i; | 
| + | 
| +            // TODO: subtract removed values from totalBytes | 
| +        } | 
| +    } | 
| + | 
| +    /** | 
| +     * Return interpolated data usage across the requested range. Interpolates | 
| +     * across buckets, so values may be rounded slightly. | 
| +     */ | 
| +    public Entry getValues(long start, long end, Entry recycle) { | 
| +        return getValues(start, end, Long.MAX_VALUE, recycle); | 
| +    } | 
| + | 
| +    /** | 
| +     * Return interpolated data usage across the requested range. Interpolates | 
| +     * across buckets, so values may be rounded slightly. | 
| +     */ | 
| +    public Entry getValues(long start, long end, long now, Entry recycle) { | 
| +        final Entry entry = recycle != null ? recycle : new Entry(); | 
| +        entry.bucketDuration = end - start; | 
| +        entry.bucketStart = start; | 
| +        entry.activeTime = activeTime != null ? 0 : UNKNOWN; | 
| +        entry.rxBytes = rxBytes != null ? 0 : UNKNOWN; | 
| +        entry.rxPackets = rxPackets != null ? 0 : UNKNOWN; | 
| +        entry.txBytes = txBytes != null ? 0 : UNKNOWN; | 
| +        entry.txPackets = txPackets != null ? 0 : UNKNOWN; | 
| +        entry.operations = operations != null ? 0 : UNKNOWN; | 
| + | 
| +        final int startIndex = getIndexAfter(end); | 
| +        for (int i = startIndex; i >= 0; i--) { | 
| +            final long curStart = bucketStart[i]; | 
| +            final long curEnd = curStart + bucketDuration; | 
| + | 
| +            // bucket is older than request; we're finished | 
| +            if (curEnd <= start) break; | 
| +            // bucket is newer than request; keep looking | 
| +            if (curStart >= end) continue; | 
| + | 
| +            // include full value for active buckets, otherwise only fractional | 
| +            final boolean activeBucket = curStart < now && curEnd > now; | 
| +            final long overlap; | 
| +            if (activeBucket) { | 
| +                overlap = bucketDuration; | 
| +            } else { | 
| +                final long overlapEnd = curEnd < end ? curEnd : end; | 
| +                final long overlapStart = curStart > start ? curStart : start; | 
| +                overlap = overlapEnd - overlapStart; | 
| +            } | 
| +            if (overlap <= 0) continue; | 
| + | 
| +            // integer math each time is faster than floating point | 
| +            if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketDuration; | 
| +            if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration; | 
| +            if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration; | 
| +            if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration; | 
| +            if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration; | 
| +            if (operations != null) entry.operations += operations[i] * overlap / bucketDuration; | 
| +        } | 
| +        return entry; | 
| +    } | 
| + | 
| +    /** | 
| +     * @deprecated only for temporary testing | 
| +     */ | 
| +    @Deprecated | 
| +    public void generateRandom(long start, long end, long bytes) { | 
| +        final Random r = new Random(); | 
| + | 
| +        final float fractionRx = r.nextFloat(); | 
| +        final long rxBytes = (long) (bytes * fractionRx); | 
| +        final long txBytes = (long) (bytes * (1 - fractionRx)); | 
| + | 
| +        final long rxPackets = rxBytes / 1024; | 
| +        final long txPackets = txBytes / 1024; | 
| +        final long operations = rxBytes / 2048; | 
| + | 
| +        generateRandom(start, end, rxBytes, rxPackets, txBytes, txPackets, operations, r); | 
| +    } | 
| + | 
| +    /** | 
| +     * @deprecated only for temporary testing | 
| +     */ | 
| +    @Deprecated | 
| +    public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes, | 
| +            long txPackets, long operations, Random r) { | 
| +        ensureBuckets(start, end); | 
| + | 
| +        final NetworkStats.Entry entry = new NetworkStats.Entry( | 
| +                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L); | 
| +        while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128 | 
| +                || operations > 32) { | 
| +            final long curStart = randomLong(r, start, end); | 
| +            final long curEnd = curStart + randomLong(r, 0, (end - curStart) / 2); | 
| + | 
| +            entry.rxBytes = randomLong(r, 0, rxBytes); | 
| +            entry.rxPackets = randomLong(r, 0, rxPackets); | 
| +            entry.txBytes = randomLong(r, 0, txBytes); | 
| +            entry.txPackets = randomLong(r, 0, txPackets); | 
| +            entry.operations = randomLong(r, 0, operations); | 
| + | 
| +            rxBytes -= entry.rxBytes; | 
| +            rxPackets -= entry.rxPackets; | 
| +            txBytes -= entry.txBytes; | 
| +            txPackets -= entry.txPackets; | 
| +            operations -= entry.operations; | 
| + | 
| +            recordData(curStart, curEnd, entry); | 
| +        } | 
| +    } | 
| + | 
| +    public static long randomLong(Random r, long start, long end) { | 
| +        return (long) (start + (r.nextFloat() * (end - start))); | 
| +    } | 
| + | 
| +    public void dump(PrintWriter pw, boolean fullHistory) { | 
| +        // TODO(bengr): Use an IndentingPrintWriter. | 
| +        pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration); | 
| + | 
| +        final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32); | 
| +        if (start > 0) { | 
| +            pw.print("(omitting "); pw.print(start); pw.println(" buckets)"); | 
| +        } | 
| + | 
| +        for (int i = start; i < bucketCount; i++) { | 
| +            pw.print("bucketStart="); pw.print(bucketStart[i]); | 
| +            if (activeTime != null) { pw.print(" activeTime="); pw.print(activeTime[i]); } | 
| +            if (rxBytes != null) { pw.print(" rxBytes="); pw.print(rxBytes[i]); } | 
| +            if (rxPackets != null) { pw.print(" rxPackets="); pw.print(rxPackets[i]); } | 
| +            if (txBytes != null) { pw.print(" txBytes="); pw.print(txBytes[i]); } | 
| +            if (txPackets != null) { pw.print(" txPackets="); pw.print(txPackets[i]); } | 
| +            if (operations != null) { pw.print(" operations="); pw.print(operations[i]); } | 
| +            pw.println(); | 
| +        } | 
| +    } | 
| + | 
| +    @Override | 
| +    public String toString() { | 
| +        final CharArrayWriter writer = new CharArrayWriter(); | 
| +        dump(new PrintWriter(writer), false); | 
| +        return writer.toString(); | 
| +    } | 
| + | 
| +    public static final Creator<NetworkStatsHistory> CREATOR = new Creator<NetworkStatsHistory>() { | 
| +        @Override | 
| +        public NetworkStatsHistory createFromParcel(Parcel in) { | 
| +            return new NetworkStatsHistory(in); | 
| +        } | 
| + | 
| +        @Override | 
| +        public NetworkStatsHistory[] newArray(int size) { | 
| +            return new NetworkStatsHistory[size]; | 
| +        } | 
| +    }; | 
| + | 
| +    private static long getLong(long[] array, int i, long value) { | 
| +        return array != null ? array[i] : value; | 
| +    } | 
| + | 
| +    private static void setLong(long[] array, int i, long value) { | 
| +        if (array != null) array[i] = value; | 
| +    } | 
| + | 
| +    private static void addLong(long[] array, int i, long value) { | 
| +        if (array != null) array[i] += value; | 
| +    } | 
| + | 
| +    public int estimateResizeBuckets(long newBucketDuration) { | 
| +        return (int) (size() * getBucketDuration() / newBucketDuration); | 
| +    } | 
| + | 
| +    /** | 
| +     * Utility methods for interacting with {@link DataInputStream} and | 
| +     * {@link DataOutputStream}, mostly dealing with writing partial arrays. | 
| +     */ | 
| +    public static class DataStreamUtils { | 
| +        @Deprecated | 
| +        public static long[] readFullLongArray(DataInputStream in) throws IOException { | 
| +            final int size = in.readInt(); | 
| +            final long[] values = new long[size]; | 
| +            for (int i = 0; i < values.length; i++) { | 
| +                values[i] = in.readLong(); | 
| +            } | 
| +            return values; | 
| +        } | 
| + | 
| +        /** | 
| +         * Read variable-length {@link Long} using protobuf-style approach. | 
| +         */ | 
| +        public static long readVarLong(DataInputStream in) throws IOException { | 
| +            int shift = 0; | 
| +            long result = 0; | 
| +            while (shift < 64) { | 
| +                byte b = in.readByte(); | 
| +                result |= (long) (b & 0x7F) << shift; | 
| +                if ((b & 0x80) == 0) | 
| +                    return result; | 
| +                shift += 7; | 
| +            } | 
| +            throw new ProtocolException("malformed long"); | 
| +        } | 
| + | 
| +        /** | 
| +         * Write variable-length {@link Long} using protobuf-style approach. | 
| +         */ | 
| +        public static void writeVarLong(DataOutputStream out, long value) throws IOException { | 
| +            while (true) { | 
| +                if ((value & ~0x7FL) == 0) { | 
| +                    out.writeByte((int) value); | 
| +                    return; | 
| +                } else { | 
| +                    out.writeByte(((int) value & 0x7F) | 0x80); | 
| +                    value >>>= 7; | 
| +                } | 
| +            } | 
| +        } | 
| + | 
| +        public static long[] readVarLongArray(DataInputStream in) throws IOException { | 
| +            final int size = in.readInt(); | 
| +            if (size == -1) return null; | 
| +            final long[] values = new long[size]; | 
| +            for (int i = 0; i < values.length; i++) { | 
| +                values[i] = readVarLong(in); | 
| +            } | 
| +            return values; | 
| +        } | 
| + | 
| +        public static void writeVarLongArray(DataOutputStream out, long[] values, int size) | 
| +                throws IOException { | 
| +            if (values == null) { | 
| +                out.writeInt(-1); | 
| +                return; | 
| +            } | 
| +            if (size > values.length) { | 
| +                throw new IllegalArgumentException("size larger than length"); | 
| +            } | 
| +            out.writeInt(size); | 
| +            for (int i = 0; i < size; i++) { | 
| +                writeVarLong(out, values[i]); | 
| +            } | 
| +        } | 
| +    } | 
| + | 
| +    /** | 
| +     * Utility methods for interacting with {@link Parcel} structures, mostly | 
| +     * dealing with writing partial arrays. | 
| +     */ | 
| +    public static class ParcelUtils { | 
| +        public static long[] readLongArray(Parcel in) { | 
| +            final int size = in.readInt(); | 
| +            if (size == -1) return null; | 
| +            final long[] values = new long[size]; | 
| +            for (int i = 0; i < values.length; i++) { | 
| +                values[i] = in.readLong(); | 
| +            } | 
| +            return values; | 
| +        } | 
| + | 
| +        public static void writeLongArray(Parcel out, long[] values, int size) { | 
| +            if (values == null) { | 
| +                out.writeInt(-1); | 
| +                return; | 
| +            } | 
| +            if (size > values.length) { | 
| +                throw new IllegalArgumentException("size larger than length"); | 
| +            } | 
| +            out.writeInt(size); | 
| +            for (int i = 0; i < size; i++) { | 
| +                out.writeLong(values[i]); | 
| +            } | 
| +        } | 
| +    } | 
| + | 
| +} | 
|  |