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
19d3e4a1e250194bbf59b1e04b60711f97001f61f8Lorenzo Colittiimport static android.net.NetworkStats.DEFAULT_NETWORK_NO;
20ada23ed56addb7c06a489c7464ae228a46588b95Lorenzo Colittiimport static android.net.NetworkStats.DEFAULT_NETWORK_YES;
21090c462be959aff120498828f82df8732bf319beJeff Sharkeyimport static android.net.NetworkStats.IFACE_ALL;
2225147878974f82f875062e99cdee85dd33f3f078Stephen Chenimport static android.net.NetworkStats.METERED_NO;
2325147878974f82f875062e99cdee85dd33f3f078Stephen Chenimport static android.net.NetworkStats.METERED_YES;
241f7e05eda687279ac9deb894f42ac927bd471ad2Jeff Davidsonimport static android.net.NetworkStats.ROAMING_NO;
251f7e05eda687279ac9deb894f42ac927bd471ad2Jeff Davidsonimport static android.net.NetworkStats.ROAMING_YES;
2663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.SET_ALL;
2763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.SET_DEFAULT;
2863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.TAG_NONE;
2963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.UID_ALL;
3063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.TrafficStats.UID_REMOVED;
3155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport static android.text.format.DateUtils.WEEK_IN_MILLIS;
3263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
33f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport static com.android.server.net.NetworkStatsService.TAG;
34f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
3563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkIdentity;
3663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkStats;
3763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkStatsHistory;
3863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.NetworkTemplate;
3963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport android.net.TrafficStats;
409c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Banimport android.os.Binder;
41da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onukiimport android.service.NetworkStatsCollectionKeyProto;
42da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onukiimport android.service.NetworkStatsCollectionProto;
43da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onukiimport android.service.NetworkStatsCollectionStatsProto;
44f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport android.telephony.SubscriptionPlan;
45090c462be959aff120498828f82df8732bf319beJeff Sharkeyimport android.text.format.DateUtils;
4655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport android.util.ArrayMap;
4739606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
489c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Banimport android.util.IntArray;
49090c462be959aff120498828f82df8732bf319beJeff Sharkeyimport android.util.MathUtils;
500fc6d03b628c8cbe80e3c2c14aaf8c6944b32d1eJeff Sharkeyimport android.util.Range;
51f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport android.util.Slog;
52da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onukiimport android.util.proto.ProtoOutputStream;
5363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
54f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport com.android.internal.annotations.VisibleForTesting;
55daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkeyimport com.android.internal.util.ArrayUtils;
5663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.android.internal.util.FileRotator;
5763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
589c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
59f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport libcore.io.IoUtils;
60f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
6163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.google.android.collect.Lists;
6263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.google.android.collect.Maps;
6363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
6463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.BufferedInputStream;
6563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.DataInputStream;
6663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.DataOutputStream;
6763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.File;
6863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.FileNotFoundException;
6963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.IOException;
7063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.InputStream;
7155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport java.io.PrintWriter;
7263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.net.ProtocolException;
73f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport java.time.ZonedDateTime;
7463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.ArrayList;
7563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.Collections;
7663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.HashMap;
77f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkeyimport java.util.Iterator;
78e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
7963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey/**
8163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * Collection of {@link NetworkStatsHistory}, stored based on combined key of
8263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
8363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey */
8463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeypublic class NetworkStatsCollection implements FileRotator.Reader {
8563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /** File header magic number: "ANET" */
8663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int FILE_MAGIC = 0x414E4554;
8763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_NETWORK_INIT = 1;
8963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_INIT = 1;
9163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_IDENT = 2;
9263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_TAG = 3;
9363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_SET = 4;
9463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UNIFIED_INIT = 16;
9663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
9863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    private final long mBucketDuration;
10063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
10163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mStartMillis;
10263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mEndMillis;
10363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mTotalBytes;
10463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private boolean mDirty;
10563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
10663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public NetworkStatsCollection(long bucketDuration) {
10763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mBucketDuration = bucketDuration;
10863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        reset();
10963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
11063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
111e0c2995d0f16246336d4385d5ebe631a60e12e86Jeff Sharkey    public void clear() {
112e0c2995d0f16246336d4385d5ebe631a60e12e86Jeff Sharkey        reset();
113e0c2995d0f16246336d4385d5ebe631a60e12e86Jeff Sharkey    }
114e0c2995d0f16246336d4385d5ebe631a60e12e86Jeff Sharkey
11563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void reset() {
11663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mStats.clear();
11763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mStartMillis = Long.MAX_VALUE;
11863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mEndMillis = Long.MIN_VALUE;
11963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mTotalBytes = 0;
12063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = false;
12163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getStartMillis() {
12463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mStartMillis;
12563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    /**
12870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * Return first atomic bucket in this collection, which is more conservative
12970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * than {@link #mStartMillis}.
13070c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     */
13170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    public long getFirstAtomicBucketMillis() {
13270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        if (mStartMillis == Long.MAX_VALUE) {
13370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            return Long.MAX_VALUE;
13470c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        } else {
13570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            return mStartMillis + mBucketDuration;
13670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        }
13770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    }
13870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
13963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getEndMillis() {
14063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mEndMillis;
14163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
14263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
14363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getTotalBytes() {
14463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mTotalBytes;
14563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
14663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
14763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public boolean isDirty() {
14863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mDirty;
14963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
15063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
15163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void clearDirty() {
15263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = false;
15363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
15463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
15563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public boolean isEmpty() {
15663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
15763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
15863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
159f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    @VisibleForTesting
160f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    public long roundUp(long time) {
161f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
162f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                || time == SubscriptionPlan.TIME_UNKNOWN) {
163f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            return time;
164f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        } else {
165f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long mod = time % mBucketDuration;
166f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            if (mod > 0) {
167f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                time -= mod;
168f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                time += mBucketDuration;
169f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            }
170f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            return time;
171f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        }
172f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    }
173f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
174f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    @VisibleForTesting
175f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    public long roundDown(long time) {
176f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
177f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                || time == SubscriptionPlan.TIME_UNKNOWN) {
178f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            return time;
179f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        } else {
180f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long mod = time % mBucketDuration;
181f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            if (mod > 0) {
182f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                time -= mod;
183f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            }
184f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            return time;
185f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        }
186f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    }
187f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
188d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey    /**
189d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey     * Safely multiple a value by a rational.
190d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey     * <p>
191d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey     * Internally it uses integer-based math whenever possible, but switches
192d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey     * over to double-based math if values would overflow.
193d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey     */
194d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey    @VisibleForTesting
195d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey    public static long multiplySafe(long value, long num, long den) {
196a7be8da9adefaf6d55f7c90b95d0c6f9e66fca63Jeff Sharkey        if (den == 0) den = 1;
197d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        long x = value;
198d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        long y = num;
199d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey
200d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        // Logic shamelessly borrowed from Math.multiplyExact()
201d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        long r = x * y;
202d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        long ax = Math.abs(x);
203d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        long ay = Math.abs(y);
204d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        if (((ax | ay) >>> 31 != 0)) {
205d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            // Some bits greater than 2^31 that might cause overflow
206d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            // Check the result using the divide operator
207d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            // and check for the special case of Long.MIN_VALUE * -1
208d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            if (((y != 0) && (r / y != x)) ||
209d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey                    (x == Long.MIN_VALUE && y == -1)) {
210d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey                // Use double math to avoid overflowing
211d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey                return (long) (((double) num / den) * value);
212d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            }
213d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        }
214d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey        return r / den;
215d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey    }
216d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey
2171efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson    public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
218cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado        return getRelevantUids(accessLevel, Binder.getCallingUid());
219cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    }
220cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado
221cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
222cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado                final int callerUid) {
2239c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        IntArray uids = new IntArray();
2249c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        for (int i = 0; i < mStats.size(); i++) {
2259c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban            final Key key = mStats.keyAt(i);
2261efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
2279c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                int j = uids.binarySearch(key.uid);
2289c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
2299c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                if (j < 0) {
2309c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                    j = ~j;
2319c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                    uids.add(j, key.uid);
2329c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                }
2339c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban            }
2349c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        }
2359c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        return uids.toArray();
2369c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban    }
2379c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
23863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
23963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Combine all {@link NetworkStatsHistory} in this collection which match
24063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * the requested parameters.
24163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
242f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey    public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan,
243f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            int uid, int set, int tag, int fields, long start, long end,
244cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado            @NetworkStatsAccess.Level int accessLevel, int callerUid) {
2451efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson        if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
2469c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban            throw new SecurityException("Network stats history of uid " + uid
2479c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                    + " is forbidden for caller " + callerUid);
2489c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        }
2499c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
250090c462be959aff120498828f82df8732bf319beJeff Sharkey        // 180 days of history should be enough for anyone; if we end up needing
251090c462be959aff120498828f82df8732bf319beJeff Sharkey        // more, we'll dynamically grow the history object.
252090c462be959aff120498828f82df8732bf319beJeff Sharkey        final int bucketEstimate = (int) MathUtils.constrain(((end - start) / mBucketDuration), 0,
253090c462be959aff120498828f82df8732bf319beJeff Sharkey                (180 * DateUtils.DAY_IN_MILLIS) / mBucketDuration);
25463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStatsHistory combined = new NetworkStatsHistory(
255f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                mBucketDuration, bucketEstimate, fields);
2569c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
2579c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        // shortcut when we know stats will be empty
2589c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        if (start == end) return combined;
2599c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
260f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        // Figure out the window of time that we should be augmenting (if any)
261f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        long augmentStart = SubscriptionPlan.TIME_UNKNOWN;
262f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        long augmentEnd = (augmentPlan != null) ? augmentPlan.getDataUsageTime()
263f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                : SubscriptionPlan.TIME_UNKNOWN;
264f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        // And if augmenting, we might need to collect more data to adjust with
265f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        long collectStart = start;
266f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        long collectEnd = end;
267f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
268f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) {
2690fc6d03b628c8cbe80e3c2c14aaf8c6944b32d1eJeff Sharkey            final Iterator<Range<ZonedDateTime>> it = augmentPlan.cycleIterator();
270f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            while (it.hasNext()) {
2710fc6d03b628c8cbe80e3c2c14aaf8c6944b32d1eJeff Sharkey                final Range<ZonedDateTime> cycle = it.next();
2720fc6d03b628c8cbe80e3c2c14aaf8c6944b32d1eJeff Sharkey                final long cycleStart = cycle.getLower().toInstant().toEpochMilli();
2730fc6d03b628c8cbe80e3c2c14aaf8c6944b32d1eJeff Sharkey                final long cycleEnd = cycle.getUpper().toInstant().toEpochMilli();
274f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) {
275f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    augmentStart = cycleStart;
276f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    collectStart = Long.min(collectStart, augmentStart);
277f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    collectEnd = Long.max(collectEnd, augmentEnd);
278f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    break;
279f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                }
280f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            }
281f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        }
282f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
283f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
284f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            // Shrink augmentation window so we don't risk undercounting.
285f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            augmentStart = roundUp(augmentStart);
286f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            augmentEnd = roundDown(augmentEnd);
287f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            // Grow collection window so we get all the stats needed.
288f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            collectStart = roundDown(collectStart);
289f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            collectEnd = roundUp(collectEnd);
290f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        }
291f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
29255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
29355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
29498170b0db191f916bf2a0f104f5d5057c2aaeaa9Wenchao Tong            if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
29563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    && templateMatches(template, key.ident)) {
29655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                final NetworkStatsHistory value = mStats.valueAt(i);
297f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                combined.recordHistory(value, collectStart, collectEnd);
29863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
29963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
30063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
301f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
302f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final NetworkStatsHistory.Entry entry = combined.getValues(
303f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    augmentStart, augmentEnd, null);
304f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
305f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            // If we don't have any recorded data for this time period, give
306f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            // ourselves something to scale with.
307f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            if (entry.rxBytes == 0 || entry.txBytes == 0) {
308f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                combined.recordData(augmentStart, augmentEnd,
309f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                        new NetworkStats.Entry(1, 0, 1, 0, 0));
310f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                combined.getValues(augmentStart, augmentEnd, entry);
311f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            }
312f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
313f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long rawBytes = entry.rxBytes + entry.txBytes;
314f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long rawRxBytes = entry.rxBytes;
315f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long rawTxBytes = entry.txBytes;
316f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long targetBytes = augmentPlan.getDataUsageBytes();
317d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            final long targetRxBytes = multiplySafe(targetBytes, rawRxBytes, rawBytes);
318d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey            final long targetTxBytes = multiplySafe(targetBytes, rawTxBytes, rawBytes);
319f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
320f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            // Scale all matching buckets to reach anchor target
321f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long beforeTotal = combined.getTotalBytes();
322f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            for (int i = 0; i < combined.size(); i++) {
323f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                combined.getValues(i, entry);
324f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                if (entry.bucketStart >= augmentStart
325f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                        && entry.bucketStart + entry.bucketDuration <= augmentEnd) {
326d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey                    entry.rxBytes = multiplySafe(targetRxBytes, entry.rxBytes, rawRxBytes);
327d405d0555ce704d0417b85884ed267ac141302deJeff Sharkey                    entry.txBytes = multiplySafe(targetTxBytes, entry.txBytes, rawTxBytes);
328f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    // We purposefully clear out packet counters to indicate
329f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    // that this data has been augmented.
330f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    entry.rxPackets = 0;
331f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    entry.txPackets = 0;
332f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    combined.setValues(i, entry);
333f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                }
334f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            }
335f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
336f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final long deltaTotal = combined.getTotalBytes() - beforeTotal;
337f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            if (deltaTotal != 0) {
338f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                Slog.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
339f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            }
340f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
341f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            // Finally we can slice data as originally requested
342f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            final NetworkStatsHistory sliced = new NetworkStatsHistory(
343f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey                    mBucketDuration, bucketEstimate, fields);
344f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            sliced.recordHistory(combined, start, end);
345f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            return sliced;
346f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        } else {
347f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey            return combined;
348f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey        }
349cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    }
350cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado
351cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    /**
352cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     * Summarize all {@link NetworkStatsHistory} in this collection which match
353cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     * the requested parameters.
354cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     */
355cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    public NetworkStats getSummary(NetworkTemplate template, long start, long end,
356cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado            @NetworkStatsAccess.Level int accessLevel, int callerUid) {
35763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final long now = System.currentTimeMillis();
35863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
35963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStats stats = new NetworkStats(end - start, 24);
360f4de294297de47d8c594956b2d8607e314e71836Jeff Sharkey
36170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        // shortcut when we know stats will be empty
36270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        if (start == end) return stats;
36370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
3649c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        final NetworkStats.Entry entry = new NetworkStats.Entry();
3659c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        NetworkStatsHistory.Entry historyEntry = null;
3669c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
36755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
36855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
3691efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            if (templateMatches(template, key.ident)
3701efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson                    && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
37198170b0db191f916bf2a0f104f5d5057c2aaeaa9Wenchao Tong                    && key.set < NetworkStats.SET_DEBUG_START) {
37255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                final NetworkStatsHistory value = mStats.valueAt(i);
37355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                historyEntry = value.getValues(start, end, now, historyEntry);
37463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.iface = IFACE_ALL;
37663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.uid = key.uid;
37763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.set = key.set;
37863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.tag = key.tag;
379d3e4a1e250194bbf59b1e04b60711f97001f61f8Lorenzo Colitti                entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ?
380d3e4a1e250194bbf59b1e04b60711f97001f61f8Lorenzo Colitti                        DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
38125147878974f82f875062e99cdee85dd33f3f078Stephen Chen                entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
3821f7e05eda687279ac9deb894f42ac927bd471ad2Jeff Davidson                entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
38363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.rxBytes = historyEntry.rxBytes;
38463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.rxPackets = historyEntry.rxPackets;
38563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.txBytes = historyEntry.txBytes;
38663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.txPackets = historyEntry.txPackets;
38763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.operations = historyEntry.operations;
38863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
38963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                if (!entry.isEmpty()) {
39063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    stats.combineValues(entry);
39163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
39263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
39363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
39463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return stats;
39663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
39763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
399bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey     * Record given {@link android.net.NetworkStats.Entry} into this collection.
40063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
40163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
40263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            long end, NetworkStats.Entry entry) {
40370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
40470c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        history.recordData(start, end, entry);
40570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
40663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
40763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
40863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
40963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Record given {@link NetworkStatsHistory} into this collection.
41063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
41163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private void recordHistory(Key key, NetworkStatsHistory history) {
41263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (history.size() == 0) return;
41363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
41463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
415ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        NetworkStatsHistory target = mStats.get(key);
416ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        if (target == null) {
417ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey            target = new NetworkStatsHistory(history.getBucketDuration());
418ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey            mStats.put(key, target);
41963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
420ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        target.recordEntireHistory(history);
42163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
42263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
42363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
42463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Record all {@link NetworkStatsHistory} contained in the given collection
42563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * into this collection.
42663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
42763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void recordCollection(NetworkStatsCollection another) {
42855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < another.mStats.size(); i++) {
42955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = another.mStats.keyAt(i);
43055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = another.mStats.valueAt(i);
43155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            recordHistory(key, value);
43263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
43363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
43463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
43563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private NetworkStatsHistory findOrCreateHistory(
43663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            NetworkIdentitySet ident, int uid, int set, int tag) {
43763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final Key key = new Key(ident, uid, set, tag);
43863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStatsHistory existing = mStats.get(key);
43963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
44063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // update when no existing, or when bucket duration changed
44163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        NetworkStatsHistory updated = null;
44263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (existing == null) {
44363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            updated = new NetworkStatsHistory(mBucketDuration, 10);
44463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } else if (existing.getBucketDuration() != mBucketDuration) {
44563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            updated = new NetworkStatsHistory(existing, mBucketDuration);
44663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
44763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
44863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (updated != null) {
44963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            mStats.put(key, updated);
45063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return updated;
45163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } else {
45263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return existing;
45363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
45463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
45563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
456bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey    @Override
45763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void read(InputStream in) throws IOException {
45863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        read(new DataInputStream(in));
45963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
46063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
46163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void read(DataInputStream in) throws IOException {
46263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // verify file magic header intact
46363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final int magic = in.readInt();
46463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (magic != FILE_MAGIC) {
46563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            throw new ProtocolException("unexpected magic: " + magic);
46663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
46763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
46863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final int version = in.readInt();
46963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        switch (version) {
47063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            case VERSION_UNIFIED_INIT: {
47163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
47263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final int identSize = in.readInt();
47363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                for (int i = 0; i < identSize; i++) {
47463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkIdentitySet ident = new NetworkIdentitySet(in);
47563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
47663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int size = in.readInt();
47763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int j = 0; j < size; j++) {
47863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int uid = in.readInt();
47963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int set = in.readInt();
48063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int tag = in.readInt();
48163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
48263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final Key key = new Key(ident, uid, set, tag);
48363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
48463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        recordHistory(key, history);
48563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
48663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
48763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                break;
48863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
48963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            default: {
49063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected version: " + version);
49163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
49263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
49363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
49463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
49563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void write(DataOutputStream out) throws IOException {
49663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // cluster key lists grouped by ident
49763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
49863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : mStats.keySet()) {
49963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            ArrayList<Key> keys = keysByIdent.get(key.ident);
50063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (keys == null) {
50163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                keys = Lists.newArrayList();
50263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                keysByIdent.put(key.ident, keys);
50363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
50463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            keys.add(key);
50563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
50663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
50763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(FILE_MAGIC);
50863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(VERSION_UNIFIED_INIT);
50963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
51063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(keysByIdent.size());
51163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (NetworkIdentitySet ident : keysByIdent.keySet()) {
51263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final ArrayList<Key> keys = keysByIdent.get(ident);
51363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            ident.writeToStream(out);
51463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
51563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            out.writeInt(keys.size());
51663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            for (Key key : keys) {
51763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final NetworkStatsHistory history = mStats.get(key);
51863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.uid);
51963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.set);
52063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.tag);
52163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                history.writeToStream(out);
52263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
52363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
52463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
52563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.flush();
52663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
52763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
52863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    @Deprecated
52963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void readLegacyNetwork(File file) throws IOException {
53063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final AtomicFile inputFile = new AtomicFile(file);
53163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
53263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        DataInputStream in = null;
53363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        try {
53463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
53563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
53663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // verify file magic header intact
53763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int magic = in.readInt();
53863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (magic != FILE_MAGIC) {
53963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected magic: " + magic);
54063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
54163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
54263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int version = in.readInt();
54363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            switch (version) {
54463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_NETWORK_INIT: {
54563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // network := size *(NetworkIdentitySet NetworkStatsHistory)
54663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int size = in.readInt();
54763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int i = 0; i < size; i++) {
54863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
54963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
55063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
55163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
55263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        recordHistory(key, history);
55363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
55463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
55563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
55663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                default: {
55763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    throw new ProtocolException("unexpected version: " + version);
55863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
55963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
56063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } catch (FileNotFoundException e) {
56163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // missing stats is okay, probably first boot
56263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } finally {
56363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            IoUtils.closeQuietly(in);
56463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
56563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
56663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
56763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    @Deprecated
56863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void readLegacyUid(File file, boolean onlyTags) throws IOException {
56963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final AtomicFile inputFile = new AtomicFile(file);
57063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
57163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        DataInputStream in = null;
57263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        try {
57363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
57463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
57563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // verify file magic header intact
57663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int magic = in.readInt();
57763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (magic != FILE_MAGIC) {
57863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected magic: " + magic);
57963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
58063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
58163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int version = in.readInt();
58263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            switch (version) {
58363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_INIT: {
58463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(UID NetworkStatsHistory)
58563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
58663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // drop this data version, since we don't have a good
58763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // mapping into NetworkIdentitySet.
58863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
58963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
59063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_IDENT: {
59163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
59263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
59363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // drop this data version, since this version only existed
59463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // for a short time.
59563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
59663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
59763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_TAG:
59863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_SET: {
59963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
60063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int identSize = in.readInt();
60163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int i = 0; i < identSize; i++) {
60263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
60363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
60463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int size = in.readInt();
60563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        for (int j = 0; j < size; j++) {
60663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int uid = in.readInt();
60763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
60863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                                    : SET_DEFAULT;
60963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int tag = in.readInt();
61063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
61163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final Key key = new Key(ident, uid, set, tag);
61263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final NetworkStatsHistory history = new NetworkStatsHistory(in);
61363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
61463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            if ((tag == TAG_NONE) != onlyTags) {
61563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                                recordHistory(key, history);
61663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            }
61763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        }
61863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
61963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
62063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
62163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                default: {
62263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    throw new ProtocolException("unexpected version: " + version);
62363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
62463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
62563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } catch (FileNotFoundException e) {
62663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // missing stats is okay, probably first boot
62763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } finally {
62863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            IoUtils.closeQuietly(in);
62963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
63063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
63163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
63263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
63363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
63463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * moving any {@link NetworkStats#TAG_NONE} series to
63563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * {@link TrafficStats#UID_REMOVED}.
63663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
637daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkey    public void removeUids(int[] uids) {
63863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final ArrayList<Key> knownKeys = Lists.newArrayList();
63963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        knownKeys.addAll(mStats.keySet());
64063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
64163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // migrate all UID stats into special "removed" bucket
64263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : knownKeys) {
643daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkey            if (ArrayUtils.contains(uids, key.uid)) {
64463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                // only migrate combined TAG_NONE history
64563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                if (key.tag == TAG_NONE) {
64663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkStatsHistory uidHistory = mStats.get(key);
64763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkStatsHistory removedHistory = findOrCreateHistory(
64863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
64963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    removedHistory.recordEntireHistory(uidHistory);
65063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
65163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mStats.remove(key);
65263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mDirty = true;
65363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
65463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
65563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
65663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
65763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
65863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (startMillis < mStartMillis) mStartMillis = startMillis;
65963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (endMillis > mEndMillis) mEndMillis = endMillis;
66063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mTotalBytes += totalBytes;
66163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = true;
66263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
66363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
66463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private int estimateBuckets() {
66555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
66663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                / mBucketDuration);
66763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
66863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
669da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki    private ArrayList<Key> getSortedKeys() {
67063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final ArrayList<Key> keys = Lists.newArrayList();
67163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        keys.addAll(mStats.keySet());
67263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        Collections.sort(keys);
673da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki        return keys;
674da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki    }
67563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
676da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki    public void dump(IndentingPrintWriter pw) {
677da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki        for (Key key : getSortedKeys()) {
67863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print("ident="); pw.print(key.ident.toString());
67963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" uid="); pw.print(key.uid);
68063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
68163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
68263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
68363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final NetworkStatsHistory history = mStats.get(key);
68463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.increaseIndent();
68563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            history.dump(pw, true);
68663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.decreaseIndent();
68763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
68863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
68963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
690da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki    public void writeToProto(ProtoOutputStream proto, long tag) {
691da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki        final long start = proto.start(tag);
692da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki
693da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki        for (Key key : getSortedKeys()) {
694da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
695da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki
696da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            // Key
697da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
698da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            key.ident.writeToProto(proto, NetworkStatsCollectionKeyProto.IDENTITY);
699da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
700da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
701da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
702da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            proto.end(startKey);
703da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki
704da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            // Value
705da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            final NetworkStatsHistory history = mStats.get(key);
706da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            history.writeToProto(proto, NetworkStatsCollectionStatsProto.HISTORY);
707da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki            proto.end(startStats);
708da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki        }
709da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki
710da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki        proto.end(start);
711da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki    }
712da65a52f87ca1a31c9e01e99756e38d37d7e7202Makoto Onuki
71355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    public void dumpCheckin(PrintWriter pw, long start, long end) {
71455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
71555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
71655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
71755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
71855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    }
71955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
72055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    /**
72155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     * Dump all contained stats that match requested parameters, but group
72255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     * together all matching {@link NetworkTemplate} under a single prefix.
72355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     */
72455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
72555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            String groupPrefix) {
72655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
72755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
72855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        // Walk through all history, grouping by matching network templates
72955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
73055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
73155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = mStats.valueAt(i);
73255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
73355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (!templateMatches(groupTemplate, key.ident)) continue;
73498170b0db191f916bf2a0f104f5d5057c2aaeaa9Wenchao Tong            if (key.set >= NetworkStats.SET_DEBUG_START) continue;
73555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
73655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key groupKey = new Key(null, key.uid, key.set, key.tag);
73755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            NetworkStatsHistory groupHistory = grouped.get(groupKey);
73855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (groupHistory == null) {
73955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                groupHistory = new NetworkStatsHistory(value.getBucketDuration());
74055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                grouped.put(groupKey, groupHistory);
74155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
74255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            groupHistory.recordHistory(value, start, end);
74355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        }
74455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
74555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < grouped.size(); i++) {
74655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = grouped.keyAt(i);
74755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = grouped.valueAt(i);
74855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
74955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (value.size() == 0) continue;
75055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
75155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print("c,");
75255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(groupPrefix); pw.print(',');
75355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(key.uid); pw.print(',');
75455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
75555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(key.tag);
75655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.println();
75755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
75855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            value.dumpCheckin(pw);
75955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        }
76055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    }
76155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
76263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
76363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
76463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * in the given {@link NetworkIdentitySet}.
76563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
76663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
76763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (NetworkIdentity ident : identSet) {
76863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (template.matches(ident)) {
76963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                return true;
77063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
77163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
77263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return false;
77363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
77463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
77563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static class Key implements Comparable<Key> {
77663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final NetworkIdentitySet ident;
77763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int uid;
77863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int set;
77963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int tag;
78063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
78163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        private final int hashCode;
78263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
78363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
78463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.ident = ident;
78563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.uid = uid;
78663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.set = set;
78763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.tag = tag;
788e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            hashCode = Objects.hash(ident, uid, set, tag);
78963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
79063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
79163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        @Override
79263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public int hashCode() {
79363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return hashCode;
79463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
79563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
79663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        @Override
79763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public boolean equals(Object obj) {
79863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (obj instanceof Key) {
79963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final Key key = (Key) obj;
80063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                return uid == key.uid && set == key.set && tag == key.tag
801e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        && Objects.equals(ident, key.ident);
80263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
80363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return false;
80463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
80563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
806bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey        @Override
80763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public int compareTo(Key another) {
80855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            int res = 0;
80955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (ident != null && another.ident != null) {
81055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = ident.compareTo(another.ident);
81155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
81255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
81355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(uid, another.uid);
81455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
81555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
81655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(set, another.set);
81755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
81855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
81955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(tag, another.tag);
82055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
82155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            return res;
82263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
82363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
82463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey}
825