163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey/*
263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * Copyright (C) 2012 The Android Open Source Project
363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey *
463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * you may not use this file except in compliance with the License.
663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * You may obtain a copy of the License at
763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey *
863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey *
1063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * Unless required by applicable law or agreed to in writing, software
1163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
1263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * See the License for the specific language governing permissions and
1463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * limitations under the License.
1563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey */
1663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
1763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeypackage com.android.server.net;
1863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
1963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.IFACE_ALL;
2063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.SET_ALL;
2163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.SET_DEFAULT;
2263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.TAG_NONE;
2363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.UID_ALL;
2463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.TrafficStats.UID_REMOVED;
2555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport static android.text.format.DateUtils.SECOND_IN_MILLIS;
2655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport static android.text.format.DateUtils.WEEK_IN_MILLIS;
2763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
2855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport android.net.ConnectivityManager;
2963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkIdentity;
3063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkStats;
3163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkStatsHistory;
3263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkTemplate;
3363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.TrafficStats;
3455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport android.util.ArrayMap;
3539606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
3663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
3755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport libcore.io.IoUtils;
3855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
39daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkeyimport com.android.internal.util.ArrayUtils;
4063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.android.internal.util.FileRotator;
4163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
4263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.google.android.collect.Lists;
4363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.google.android.collect.Maps;
4463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
4563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.BufferedInputStream;
4663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.DataInputStream;
4763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.DataOutputStream;
4863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.File;
4963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.FileNotFoundException;
5063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.IOException;
5163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.InputStream;
5255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport java.io.PrintWriter;
5363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.net.ProtocolException;
5463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.ArrayList;
5563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.Collections;
5663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.HashMap;
57e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
5863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
5963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey/**
6063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * Collection of {@link NetworkStatsHistory}, stored based on combined key of
6163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
6263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey */
6363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeypublic class NetworkStatsCollection implements FileRotator.Reader {
6463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /** File header magic number: "ANET" */
6563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int FILE_MAGIC = 0x414E4554;
6663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
6763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_NETWORK_INIT = 1;
6863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
6963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_INIT = 1;
7063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_IDENT = 2;
7163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_TAG = 3;
7263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_SET = 4;
7363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UNIFIED_INIT = 16;
7563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
7763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    private final long mBucketDuration;
7963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mStartMillis;
8163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mEndMillis;
8263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mTotalBytes;
8363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private boolean mDirty;
8463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public NetworkStatsCollection(long bucketDuration) {
8663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mBucketDuration = bucketDuration;
8763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        reset();
8863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
8963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void reset() {
9163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mStats.clear();
9263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mStartMillis = Long.MAX_VALUE;
9363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mEndMillis = Long.MIN_VALUE;
9463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mTotalBytes = 0;
9563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = false;
9663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
9763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getStartMillis() {
9963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mStartMillis;
10063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
10163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
10270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    /**
10370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * Return first atomic bucket in this collection, which is more conservative
10470c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * than {@link #mStartMillis}.
10570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     */
10670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    public long getFirstAtomicBucketMillis() {
10770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        if (mStartMillis == Long.MAX_VALUE) {
10870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            return Long.MAX_VALUE;
10970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        } else {
11070c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            return mStartMillis + mBucketDuration;
11170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        }
11270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    }
11370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
11463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getEndMillis() {
11563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mEndMillis;
11663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
11763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
11863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getTotalBytes() {
11963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mTotalBytes;
12063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public boolean isDirty() {
12363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mDirty;
12463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void clearDirty() {
12763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = false;
12863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
13063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public boolean isEmpty() {
13163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
13263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
13363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
13463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
13563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Combine all {@link NetworkStatsHistory} in this collection which match
13663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * the requested parameters.
13763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
13863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public NetworkStatsHistory getHistory(
13963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            NetworkTemplate template, int uid, int set, int tag, int fields) {
14070c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE);
14170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    }
14270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
14370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    /**
14470c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * Combine all {@link NetworkStatsHistory} in this collection which match
14570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * the requested parameters.
14670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     */
14770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    public NetworkStatsHistory getHistory(
14870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) {
14963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStatsHistory combined = new NetworkStatsHistory(
15063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mBucketDuration, estimateBuckets(), fields);
15155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
15255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
15363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final boolean setMatches = set == SET_ALL || key.set == set;
15463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (key.uid == uid && setMatches && key.tag == tag
15563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    && templateMatches(template, key.ident)) {
15655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                final NetworkStatsHistory value = mStats.valueAt(i);
15755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                combined.recordHistory(value, start, end);
15863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
15963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
16063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return combined;
16163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
16263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
16363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
16463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Summarize all {@link NetworkStatsHistory} in this collection which match
16563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * the requested parameters.
16663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
16763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public NetworkStats getSummary(NetworkTemplate template, long start, long end) {
16863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final long now = System.currentTimeMillis();
16963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
17063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStats stats = new NetworkStats(end - start, 24);
17163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStats.Entry entry = new NetworkStats.Entry();
17263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        NetworkStatsHistory.Entry historyEntry = null;
17363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
17470c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        // shortcut when we know stats will be empty
17570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        if (start == end) return stats;
17670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
17755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
17855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
17963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (templateMatches(template, key.ident)) {
18055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                final NetworkStatsHistory value = mStats.valueAt(i);
18155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                historyEntry = value.getValues(start, end, now, historyEntry);
18263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
18363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.iface = IFACE_ALL;
18463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.uid = key.uid;
18563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.set = key.set;
18663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.tag = key.tag;
18763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.rxBytes = historyEntry.rxBytes;
18863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.rxPackets = historyEntry.rxPackets;
18963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.txBytes = historyEntry.txBytes;
19063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.txPackets = historyEntry.txPackets;
19163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.operations = historyEntry.operations;
19263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
19363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                if (!entry.isEmpty()) {
19463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    stats.combineValues(entry);
19563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
19663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
19763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
19863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
19963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return stats;
20063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
20163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
20263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
203bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey     * Record given {@link android.net.NetworkStats.Entry} into this collection.
20463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
20563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
20663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            long end, NetworkStats.Entry entry) {
20770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
20870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        history.recordData(start, end, entry);
20970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
21063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
21163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
21263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
21363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Record given {@link NetworkStatsHistory} into this collection.
21463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
21563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private void recordHistory(Key key, NetworkStatsHistory history) {
21663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (history.size() == 0) return;
21763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
21863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
219ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        NetworkStatsHistory target = mStats.get(key);
220ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        if (target == null) {
221ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey            target = new NetworkStatsHistory(history.getBucketDuration());
222ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey            mStats.put(key, target);
22363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
224ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        target.recordEntireHistory(history);
22563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
22663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
22763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
22863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Record all {@link NetworkStatsHistory} contained in the given collection
22963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * into this collection.
23063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
23163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void recordCollection(NetworkStatsCollection another) {
23255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < another.mStats.size(); i++) {
23355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = another.mStats.keyAt(i);
23455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = another.mStats.valueAt(i);
23555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            recordHistory(key, value);
23663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
23763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
23863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
23963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private NetworkStatsHistory findOrCreateHistory(
24063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            NetworkIdentitySet ident, int uid, int set, int tag) {
24163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final Key key = new Key(ident, uid, set, tag);
24263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStatsHistory existing = mStats.get(key);
24363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
24463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // update when no existing, or when bucket duration changed
24563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        NetworkStatsHistory updated = null;
24663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (existing == null) {
24763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            updated = new NetworkStatsHistory(mBucketDuration, 10);
24863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } else if (existing.getBucketDuration() != mBucketDuration) {
24963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            updated = new NetworkStatsHistory(existing, mBucketDuration);
25063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
25163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
25263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (updated != null) {
25363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            mStats.put(key, updated);
25463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return updated;
25563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } else {
25663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return existing;
25763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
25863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
25963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
260bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey    @Override
26163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void read(InputStream in) throws IOException {
26263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        read(new DataInputStream(in));
26363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
26463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
26563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void read(DataInputStream in) throws IOException {
26663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // verify file magic header intact
26763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final int magic = in.readInt();
26863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (magic != FILE_MAGIC) {
26963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            throw new ProtocolException("unexpected magic: " + magic);
27063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
27163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
27263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final int version = in.readInt();
27363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        switch (version) {
27463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            case VERSION_UNIFIED_INIT: {
27563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
27663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final int identSize = in.readInt();
27763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                for (int i = 0; i < identSize; i++) {
27863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkIdentitySet ident = new NetworkIdentitySet(in);
27963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
28063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int size = in.readInt();
28163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int j = 0; j < size; j++) {
28263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int uid = in.readInt();
28363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int set = in.readInt();
28463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int tag = in.readInt();
28563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
28663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final Key key = new Key(ident, uid, set, tag);
28763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
28863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        recordHistory(key, history);
28963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
29063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
29163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                break;
29263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
29363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            default: {
29463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected version: " + version);
29563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
29663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
29763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
29863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
29963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void write(DataOutputStream out) throws IOException {
30063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // cluster key lists grouped by ident
30163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
30263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : mStats.keySet()) {
30363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            ArrayList<Key> keys = keysByIdent.get(key.ident);
30463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (keys == null) {
30563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                keys = Lists.newArrayList();
30663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                keysByIdent.put(key.ident, keys);
30763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
30863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            keys.add(key);
30963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
31063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
31163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(FILE_MAGIC);
31263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(VERSION_UNIFIED_INIT);
31363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
31463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(keysByIdent.size());
31563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (NetworkIdentitySet ident : keysByIdent.keySet()) {
31663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final ArrayList<Key> keys = keysByIdent.get(ident);
31763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            ident.writeToStream(out);
31863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
31963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            out.writeInt(keys.size());
32063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            for (Key key : keys) {
32163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final NetworkStatsHistory history = mStats.get(key);
32263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.uid);
32363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.set);
32463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.tag);
32563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                history.writeToStream(out);
32663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
32763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
32863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
32963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.flush();
33063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
33163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
33263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    @Deprecated
33363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void readLegacyNetwork(File file) throws IOException {
33463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final AtomicFile inputFile = new AtomicFile(file);
33563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
33663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        DataInputStream in = null;
33763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        try {
33863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
33963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
34063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // verify file magic header intact
34163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int magic = in.readInt();
34263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (magic != FILE_MAGIC) {
34363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected magic: " + magic);
34463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
34563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
34663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int version = in.readInt();
34763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            switch (version) {
34863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_NETWORK_INIT: {
34963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // network := size *(NetworkIdentitySet NetworkStatsHistory)
35063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int size = in.readInt();
35163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int i = 0; i < size; i++) {
35263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
35363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
35463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
35563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
35663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        recordHistory(key, history);
35763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
35863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
35963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
36063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                default: {
36163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    throw new ProtocolException("unexpected version: " + version);
36263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
36363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
36463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } catch (FileNotFoundException e) {
36563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // missing stats is okay, probably first boot
36663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } finally {
36763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            IoUtils.closeQuietly(in);
36863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
36963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
37063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    @Deprecated
37263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void readLegacyUid(File file, boolean onlyTags) throws IOException {
37363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final AtomicFile inputFile = new AtomicFile(file);
37463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        DataInputStream in = null;
37663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        try {
37763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
37863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // verify file magic header intact
38063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int magic = in.readInt();
38163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (magic != FILE_MAGIC) {
38263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected magic: " + magic);
38363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
38463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
38563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int version = in.readInt();
38663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            switch (version) {
38763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_INIT: {
38863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(UID NetworkStatsHistory)
38963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // drop this data version, since we don't have a good
39163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // mapping into NetworkIdentitySet.
39263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
39363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
39463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_IDENT: {
39563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
39663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // drop this data version, since this version only existed
39863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // for a short time.
39963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
40063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
40163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_TAG:
40263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_SET: {
40363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
40463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int identSize = in.readInt();
40563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int i = 0; i < identSize; i++) {
40663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
40763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
40863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int size = in.readInt();
40963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        for (int j = 0; j < size; j++) {
41063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int uid = in.readInt();
41163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
41263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                                    : SET_DEFAULT;
41363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int tag = in.readInt();
41463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
41563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final Key key = new Key(ident, uid, set, tag);
41663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final NetworkStatsHistory history = new NetworkStatsHistory(in);
41763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
41863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            if ((tag == TAG_NONE) != onlyTags) {
41963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                                recordHistory(key, history);
42063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            }
42163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        }
42263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
42363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
42463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
42563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                default: {
42663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    throw new ProtocolException("unexpected version: " + version);
42763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
42863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
42963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } catch (FileNotFoundException e) {
43063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // missing stats is okay, probably first boot
43163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } finally {
43263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            IoUtils.closeQuietly(in);
43363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
43463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
43563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
43663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
43763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
43863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * moving any {@link NetworkStats#TAG_NONE} series to
43963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * {@link TrafficStats#UID_REMOVED}.
44063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
441daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkey    public void removeUids(int[] uids) {
44263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final ArrayList<Key> knownKeys = Lists.newArrayList();
44363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        knownKeys.addAll(mStats.keySet());
44463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
44563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // migrate all UID stats into special "removed" bucket
44663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : knownKeys) {
447daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkey            if (ArrayUtils.contains(uids, key.uid)) {
44863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                // only migrate combined TAG_NONE history
44963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                if (key.tag == TAG_NONE) {
45063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkStatsHistory uidHistory = mStats.get(key);
45163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkStatsHistory removedHistory = findOrCreateHistory(
45263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
45363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    removedHistory.recordEntireHistory(uidHistory);
45463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
45563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mStats.remove(key);
45663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mDirty = true;
45763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
45863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
45963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
46063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
46163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
46263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (startMillis < mStartMillis) mStartMillis = startMillis;
46363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (endMillis > mEndMillis) mEndMillis = endMillis;
46463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mTotalBytes += totalBytes;
46563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = true;
46663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
46763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
46863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private int estimateBuckets() {
46955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
47063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                / mBucketDuration);
47163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
47263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
47363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void dump(IndentingPrintWriter pw) {
47463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final ArrayList<Key> keys = Lists.newArrayList();
47563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        keys.addAll(mStats.keySet());
47663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        Collections.sort(keys);
47763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
47863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : keys) {
47963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print("ident="); pw.print(key.ident.toString());
48063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" uid="); pw.print(key.uid);
48163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
48263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
48363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
48463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final NetworkStatsHistory history = mStats.get(key);
48563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.increaseIndent();
48663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            history.dump(pw, true);
48763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.decreaseIndent();
48863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
48963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
49063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
49155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    public void dumpCheckin(PrintWriter pw, long start, long end) {
49255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
49355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
49455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
49555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
49655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    }
49755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
49855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    /**
49955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     * Dump all contained stats that match requested parameters, but group
50055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     * together all matching {@link NetworkTemplate} under a single prefix.
50155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     */
50255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
50355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            String groupPrefix) {
50455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
50555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
50655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        // Walk through all history, grouping by matching network templates
50755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
50855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
50955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = mStats.valueAt(i);
51055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
51155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (!templateMatches(groupTemplate, key.ident)) continue;
51255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
51355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key groupKey = new Key(null, key.uid, key.set, key.tag);
51455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            NetworkStatsHistory groupHistory = grouped.get(groupKey);
51555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (groupHistory == null) {
51655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                groupHistory = new NetworkStatsHistory(value.getBucketDuration());
51755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                grouped.put(groupKey, groupHistory);
51855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
51955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            groupHistory.recordHistory(value, start, end);
52055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        }
52155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
52255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < grouped.size(); i++) {
52355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = grouped.keyAt(i);
52455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = grouped.valueAt(i);
52555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
52655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (value.size() == 0) continue;
52755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
52855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print("c,");
52955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(groupPrefix); pw.print(',');
53055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(key.uid); pw.print(',');
53155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
53255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(key.tag);
53355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.println();
53455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
53555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            value.dumpCheckin(pw);
53655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        }
53755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    }
53855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
53963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
54063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
54163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * in the given {@link NetworkIdentitySet}.
54263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
54363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
54463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (NetworkIdentity ident : identSet) {
54563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (template.matches(ident)) {
54663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                return true;
54763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
54863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
54963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return false;
55063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
55163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
55263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static class Key implements Comparable<Key> {
55363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final NetworkIdentitySet ident;
55463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int uid;
55563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int set;
55663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int tag;
55763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
55863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        private final int hashCode;
55963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
56063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
56163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.ident = ident;
56263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.uid = uid;
56363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.set = set;
56463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.tag = tag;
565e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            hashCode = Objects.hash(ident, uid, set, tag);
56663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
56763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
56863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        @Override
56963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public int hashCode() {
57063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return hashCode;
57163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
57263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
57363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        @Override
57463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public boolean equals(Object obj) {
57563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (obj instanceof Key) {
57663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final Key key = (Key) obj;
57763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                return uid == key.uid && set == key.set && tag == key.tag
578e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        && Objects.equals(ident, key.ident);
57963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
58063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return false;
58163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
58263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
583bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey        @Override
58463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public int compareTo(Key another) {
58555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            int res = 0;
58655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (ident != null && another.ident != null) {
58755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = ident.compareTo(another.ident);
58855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
58955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
59055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(uid, another.uid);
59155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
59255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
59355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(set, another.set);
59455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
59555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
59655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(tag, another.tag);
59755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
59855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            return res;
59963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
60063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
60163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey}
602