NetworkStatsHistory.java revision d37948f6ed1667d077e0e3a38808f42f981ddcc2
175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey/*
275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * Copyright (C) 2011 The Android Open Source Project
375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey *
475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * you may not use this file except in compliance with the License.
675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * You may obtain a copy of the License at
775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey *
875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey *
1075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
1175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
1275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * See the License for the specific language governing permissions and
1475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * limitations under the License.
1575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey */
1675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
1775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeypackage android.net;
1875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
1975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport android.os.Parcel;
2075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport android.os.Parcelable;
2175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
2275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport java.io.CharArrayWriter;
2375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport java.io.DataInputStream;
2475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport java.io.DataOutputStream;
2575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport java.io.IOException;
2675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport java.io.PrintWriter;
2761ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkeyimport java.net.ProtocolException;
2875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeyimport java.util.Arrays;
2961ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkeyimport java.util.Random;
3075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
3175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey/**
3275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * Collection of historical network statistics, recorded into equally-sized
3375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * "buckets" in time. Internally it stores data in {@code long} series for more
3475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * efficient persistence.
3575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * <p>
3675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * Each bucket is defined by a {@link #bucketStart} timestamp, and lasts for
3775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * {@link #bucketDuration}. Internally assumes that {@link #bucketStart} is
3875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * sorted at all times.
3975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey *
4075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey * @hide
4175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey */
4275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkeypublic class NetworkStatsHistory implements Parcelable {
431b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey    private static final int VERSION_INIT = 1;
4475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
451b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey    // TODO: teach about varint encoding to use less disk space
46d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    // TODO: extend to record rxPackets/txPackets
47d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey
48d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    private final long bucketDuration;
49d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    private int bucketCount;
50d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    private long[] bucketStart;
51d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    private long[] rxBytes;
52d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    private long[] txBytes;
53d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey
54d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    public static class Entry {
55d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        public long bucketStart;
56d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        public long bucketDuration;
57d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        public long rxBytes;
58d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        public long txBytes;
59d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    }
6075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
61d2a458750e5a3d490af09cecb5c28370baf0a913Jeff Sharkey    public NetworkStatsHistory(long bucketDuration) {
624a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey        this(bucketDuration, 10);
634a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey    }
644a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey
654a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey    public NetworkStatsHistory(long bucketDuration, int initialSize) {
6675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        this.bucketDuration = bucketDuration;
674a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey        bucketStart = new long[initialSize];
68d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        rxBytes = new long[initialSize];
69d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        txBytes = new long[initialSize];
704a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey        bucketCount = 0;
7175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
7275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
7375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public NetworkStatsHistory(Parcel in) {
7475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        bucketDuration = in.readLong();
7575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        bucketStart = readLongArray(in);
76d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        rxBytes = in.createLongArray();
77d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        txBytes = in.createLongArray();
7875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        bucketCount = bucketStart.length;
7975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
8075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
8175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    /** {@inheritDoc} */
8275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public void writeToParcel(Parcel out, int flags) {
8375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        out.writeLong(bucketDuration);
8475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        writeLongArray(out, bucketStart, bucketCount);
85d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        writeLongArray(out, rxBytes, bucketCount);
86d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        writeLongArray(out, txBytes, bucketCount);
8775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
8875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
8975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public NetworkStatsHistory(DataInputStream in) throws IOException {
9075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final int version = in.readInt();
9161ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        switch (version) {
921b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey            case VERSION_INIT: {
9361ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey                bucketDuration = in.readLong();
9461ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey                bucketStart = readLongArray(in);
95d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey                rxBytes = readLongArray(in);
96d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey                txBytes = readLongArray(in);
9761ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey                bucketCount = bucketStart.length;
9861ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey                break;
9961ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            }
10061ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            default: {
10161ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey                throw new ProtocolException("unexpected version: " + version);
10261ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            }
10361ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        }
10475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
10575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
10675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public void writeToStream(DataOutputStream out) throws IOException {
1071b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey        out.writeInt(VERSION_INIT);
10875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        out.writeLong(bucketDuration);
10975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        writeLongArray(out, bucketStart, bucketCount);
110d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        writeLongArray(out, rxBytes, bucketCount);
111d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        writeLongArray(out, txBytes, bucketCount);
11275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
11375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
11475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    /** {@inheritDoc} */
11575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public int describeContents() {
11675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        return 0;
11775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
11875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
119d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    public int size() {
120d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        return bucketCount;
121d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    }
122d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey
123d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    public long getBucketDuration() {
124d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        return bucketDuration;
125d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    }
126d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey
127d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    /**
128d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey     * Return specific stats entry.
129d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey     */
130d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    public Entry getValues(int i, Entry recycle) {
131d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        final Entry entry = recycle != null ? recycle : new Entry();
132d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        entry.bucketStart = bucketStart[i];
133d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        entry.bucketDuration = bucketDuration;
134d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        entry.rxBytes = rxBytes[i];
135d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        entry.txBytes = txBytes[i];
136d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        return entry;
137d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey    }
138d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey
13975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    /**
14075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     * Record that data traffic occurred in the given time range. Will
14175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     * distribute across internal buckets, creating new buckets as needed.
14275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     */
14375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public void recordData(long start, long end, long rx, long tx) {
1441b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey        if (rx < 0 || tx < 0) {
1451b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey            throw new IllegalArgumentException(
1461b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey                    "tried recording negative data: rx=" + rx + ", tx=" + tx);
1471b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey        }
1481b5a2a96f793211bfbd39aa29cc41031dfa23950Jeff Sharkey
14975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        // create any buckets needed by this range
15075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        ensureBuckets(start, end);
15175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
15275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        // distribute data usage into buckets
15375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final long duration = end - start;
15475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (int i = bucketCount - 1; i >= 0; i--) {
15575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final long curStart = bucketStart[i];
15675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final long curEnd = curStart + bucketDuration;
15775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
15875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            // bucket is older than record; we're finished
15975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            if (curEnd < start) break;
16075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            // bucket is newer than record; keep looking
16175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            if (curStart > end) continue;
16275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
16375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
16475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            if (overlap > 0) {
165d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey                this.rxBytes[i] += rx * overlap / duration;
166d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey                this.txBytes[i] += tx * overlap / duration;
16775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            }
16875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
16975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
17075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
17175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    /**
17219862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey     * Record an entire {@link NetworkStatsHistory} into this history. Usually
17319862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey     * for combining together stats for external reporting.
17419862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey     */
17519862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey    public void recordEntireHistory(NetworkStatsHistory input) {
17619862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        for (int i = 0; i < input.bucketCount; i++) {
17719862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            final long start = input.bucketStart[i];
17819862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            final long end = start + input.bucketDuration;
179d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            recordData(start, end, input.rxBytes[i], input.txBytes[i]);
18019862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        }
18119862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey    }
18219862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey
18319862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey    /**
18475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     * Ensure that buckets exist for given time range, creating as needed.
18575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     */
18675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    private void ensureBuckets(long start, long end) {
18775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        // normalize incoming range to bucket boundaries
18875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        start -= start % bucketDuration;
18975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        end += (bucketDuration - (end % bucketDuration)) % bucketDuration;
19075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
19175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (long now = start; now < end; now += bucketDuration) {
19275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            // try finding existing bucket
19375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final int index = Arrays.binarySearch(bucketStart, 0, bucketCount, now);
19475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            if (index < 0) {
19575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey                // bucket missing, create and insert
19675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey                insertBucket(~index, now);
19775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            }
19875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
19975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
20075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
20175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    /**
20275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     * Insert new bucket at requested index and starting time.
20375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     */
20475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    private void insertBucket(int index, long start) {
20575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        // create more buckets when needed
2064a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey        if (bucketCount >= bucketStart.length) {
2074a97122ebf4d92a3f94402041729d77905e6c0c0Jeff Sharkey            final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
20875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            bucketStart = Arrays.copyOf(bucketStart, newLength);
209d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            rxBytes = Arrays.copyOf(rxBytes, newLength);
210d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            txBytes = Arrays.copyOf(txBytes, newLength);
21175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
21275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
21375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        // create gap when inserting bucket in middle
21475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        if (index < bucketCount) {
21575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final int dstPos = index + 1;
21675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final int length = bucketCount - index;
21775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
21875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
219d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
220d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            System.arraycopy(txBytes, index, txBytes, dstPos, length);
22175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
22275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
22375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        bucketStart[index] = start;
224d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        rxBytes[index] = 0;
225d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey        txBytes[index] = 0;
22675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        bucketCount++;
22775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
22875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
22975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    /**
23075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     * Remove buckets older than requested cutoff.
23175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey     */
23275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public void removeBucketsBefore(long cutoff) {
23375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        int i;
23475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (i = 0; i < bucketCount; i++) {
23575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final long curStart = bucketStart[i];
23675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final long curEnd = curStart + bucketDuration;
23775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
23875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            // cutoff happens before or during this bucket; everything before
23975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            // this bucket should be removed.
24075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            if (curEnd > cutoff) break;
24175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
24275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
24375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        if (i > 0) {
24475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            final int length = bucketStart.length;
24575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            bucketStart = Arrays.copyOfRange(bucketStart, i, length);
246d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            rxBytes = Arrays.copyOfRange(rxBytes, i, length);
247d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            txBytes = Arrays.copyOfRange(txBytes, i, length);
24875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            bucketCount -= i;
24975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
25075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
25175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
25261ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey    /**
25319862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey     * Return interpolated data usage across the requested range. Interpolates
25419862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey     * across buckets, so values may be rounded slightly.
25519862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey     */
2563f3913550c10792edb8aecf66cc83c3db5c8b311Jeff Sharkey    public long[] getTotalData(long start, long end, long[] outTotal) {
25719862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        long rx = 0;
25819862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        long tx = 0;
25919862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey
26019862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        for (int i = bucketCount - 1; i >= 0; i--) {
26119862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            final long curStart = bucketStart[i];
26219862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            final long curEnd = curStart + bucketDuration;
26319862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey
26419862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            // bucket is older than record; we're finished
26519862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            if (curEnd < start) break;
26619862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            // bucket is newer than record; keep looking
26719862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            if (curStart > end) continue;
26819862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey
26919862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
27019862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            if (overlap > 0) {
271d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey                rx += this.rxBytes[i] * overlap / bucketDuration;
272d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey                tx += this.txBytes[i] * overlap / bucketDuration;
27319862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey            }
27419862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        }
27519862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey
2763f3913550c10792edb8aecf66cc83c3db5c8b311Jeff Sharkey        if (outTotal == null || outTotal.length != 2) {
2773f3913550c10792edb8aecf66cc83c3db5c8b311Jeff Sharkey            outTotal = new long[2];
2783f3913550c10792edb8aecf66cc83c3db5c8b311Jeff Sharkey        }
27919862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        outTotal[0] = rx;
28019862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey        outTotal[1] = tx;
2813f3913550c10792edb8aecf66cc83c3db5c8b311Jeff Sharkey        return outTotal;
28219862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey    }
28319862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey
28419862bf5d058b6ab0c2979e7a5e0297dae6b170bJeff Sharkey    /**
28561ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey     * @deprecated only for temporary testing
28661ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey     */
28761ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey    @Deprecated
28861ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey    public void generateRandom(long start, long end, long rx, long tx) {
28961ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        ensureBuckets(start, end);
29061ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey
29161ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        final Random r = new Random();
29261ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        while (rx > 1024 && tx > 1024) {
29361ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            final long curStart = randomLong(r, start, end);
29461ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            final long curEnd = randomLong(r, curStart, end);
29561ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            final long curRx = randomLong(r, 0, rx);
29661ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            final long curTx = randomLong(r, 0, tx);
29761ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey
29861ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            recordData(curStart, curEnd, curRx, curTx);
29961ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey
30061ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            rx -= curRx;
30161ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            tx -= curTx;
30261ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        }
30361ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey    }
30461ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey
30561ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey    private static long randomLong(Random r, long start, long end) {
30661ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        return (long) (start + (r.nextFloat() * (end - start)));
30761ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey    }
30861ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey
309350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey    public void dump(String prefix, PrintWriter pw, boolean fullHistory) {
31075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        pw.print(prefix);
31161ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey        pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
312350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey
313350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey        final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
314350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey        if (start > 0) {
315350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey            pw.print(prefix);
316350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey            pw.print("  (omitting "); pw.print(start); pw.println(" buckets)");
317350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey        }
318350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey
319350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey        for (int i = start; i < bucketCount; i++) {
32075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            pw.print(prefix);
32161ee0bbb5b87fb5c4c3dc219868d52743def3d2bJeff Sharkey            pw.print("  bucketStart="); pw.print(bucketStart[i]);
322d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            pw.print(" rxBytes="); pw.print(rxBytes[i]);
323d37948f6ed1667d077e0e3a38808f42f981ddcc2Jeff Sharkey            pw.print(" txBytes="); pw.println(txBytes[i]);
32475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
32575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
32675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
32775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    @Override
32875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public String toString() {
32975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final CharArrayWriter writer = new CharArrayWriter();
330350083e36b9db6062e165954403ef921ff3dfdadJeff Sharkey        dump("", new PrintWriter(writer), false);
33175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        return writer.toString();
33275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
33375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
33475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    public static final Creator<NetworkStatsHistory> CREATOR = new Creator<NetworkStatsHistory>() {
33575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        public NetworkStatsHistory createFromParcel(Parcel in) {
33675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            return new NetworkStatsHistory(in);
33775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
33875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
33975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        public NetworkStatsHistory[] newArray(int size) {
34075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            return new NetworkStatsHistory[size];
34175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
34275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    };
34375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
34475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    private static long[] readLongArray(DataInputStream in) throws IOException {
34575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final int size = in.readInt();
34675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final long[] values = new long[size];
34775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (int i = 0; i < values.length; i++) {
34875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            values[i] = in.readLong();
34975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
35075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        return values;
35175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
35275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
35375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    private static void writeLongArray(DataOutputStream out, long[] values, int size) throws IOException {
35475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        if (size > values.length) {
35575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            throw new IllegalArgumentException("size larger than length");
35675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
35775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        out.writeInt(size);
35875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (int i = 0; i < size; i++) {
35975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            out.writeLong(values[i]);
36075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
36175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
36275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
36375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    private static long[] readLongArray(Parcel in) {
36475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final int size = in.readInt();
36575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        final long[] values = new long[size];
36675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (int i = 0; i < values.length; i++) {
36775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            values[i] = in.readLong();
36875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
36975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        return values;
37075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
37175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
37275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    private static void writeLongArray(Parcel out, long[] values, int size) {
37375279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        if (size > values.length) {
37475279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            throw new IllegalArgumentException("size larger than length");
37575279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
37675279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        out.writeInt(size);
37775279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        for (int i = 0; i < size; i++) {
37875279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey            out.writeLong(values[i]);
37975279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey        }
38075279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey    }
38175279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey
38275279904202357565cf5a1cb11148d01f42b4569Jeff Sharkey}
383