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;
201f7e05eda687279ac9deb894f42ac927bd471ad2Jeff Davidsonimport static android.net.NetworkStats.ROAMING_NO;
211f7e05eda687279ac9deb894f42ac927bd471ad2Jeff Davidsonimport static android.net.NetworkStats.ROAMING_YES;
2263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.SET_ALL;
2363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.SET_DEFAULT;
2463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.TAG_NONE;
2563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.NetworkStats.UID_ALL;
2663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport static android.net.TrafficStats.UID_REMOVED;
2755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport static android.text.format.DateUtils.WEEK_IN_MILLIS;
2863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
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;
349c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Banimport android.os.Binder;
3555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport android.util.ArrayMap;
3639606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile;
379c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Banimport android.util.IntArray;
3863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkeyimport com.android.internal.util.ArrayUtils;
4063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.android.internal.util.FileRotator;
4163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
429c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
4363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.google.android.collect.Lists;
4463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport com.google.android.collect.Maps;
4563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
461efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidsonimport libcore.io.IoUtils;
471efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson
4863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.BufferedInputStream;
4963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.DataInputStream;
5063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.DataOutputStream;
5163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.File;
5263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.FileNotFoundException;
5363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.IOException;
5463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.io.InputStream;
5555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkeyimport java.io.PrintWriter;
5663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.net.ProtocolException;
5763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.ArrayList;
5863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.Collections;
5963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeyimport java.util.HashMap;
60e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
6163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
6263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey/**
6363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * Collection of {@link NetworkStatsHistory}, stored based on combined key of
6463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
6563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey */
6663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkeypublic class NetworkStatsCollection implements FileRotator.Reader {
6763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /** File header magic number: "ANET" */
6863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int FILE_MAGIC = 0x414E4554;
6963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_NETWORK_INIT = 1;
7163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_INIT = 1;
7363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_IDENT = 2;
7463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_TAG = 3;
7563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UID_WITH_SET = 4;
7663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static final int VERSION_UNIFIED_INIT = 16;
7863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
7955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
8063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    private final long mBucketDuration;
8263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mStartMillis;
8463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mEndMillis;
8563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private long mTotalBytes;
8663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private boolean mDirty;
8763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
8863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public NetworkStatsCollection(long bucketDuration) {
8963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mBucketDuration = bucketDuration;
9063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        reset();
9163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
9263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
9363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void reset() {
9463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mStats.clear();
9563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mStartMillis = Long.MAX_VALUE;
9663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mEndMillis = Long.MIN_VALUE;
9763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mTotalBytes = 0;
9863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = false;
9963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
10063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
10163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getStartMillis() {
10263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mStartMillis;
10363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
10463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
10570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    /**
10670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * Return first atomic bucket in this collection, which is more conservative
10770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * than {@link #mStartMillis}.
10870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     */
10970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    public long getFirstAtomicBucketMillis() {
11070c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        if (mStartMillis == Long.MAX_VALUE) {
11170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            return Long.MAX_VALUE;
11270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        } else {
11370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey            return mStartMillis + mBucketDuration;
11470c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        }
11570c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    }
11670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
11763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getEndMillis() {
11863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mEndMillis;
11963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public long getTotalBytes() {
12263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mTotalBytes;
12363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public boolean isDirty() {
12663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mDirty;
12763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
12863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
12963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void clearDirty() {
13063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = false;
13163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
13263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
13363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public boolean isEmpty() {
13463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
13563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
13663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
1371efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson    public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
138cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado        return getRelevantUids(accessLevel, Binder.getCallingUid());
139cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    }
140cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado
141cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
142cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado                final int callerUid) {
1439c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        IntArray uids = new IntArray();
1449c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        for (int i = 0; i < mStats.size(); i++) {
1459c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban            final Key key = mStats.keyAt(i);
1461efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
1479c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                int j = uids.binarySearch(key.uid);
1489c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
1499c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                if (j < 0) {
1509c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                    j = ~j;
1519c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                    uids.add(j, key.uid);
1529c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                }
1539c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban            }
1549c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        }
1559c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        return uids.toArray();
1569c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban    }
1579c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
15863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
15963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Combine all {@link NetworkStatsHistory} in this collection which match
16063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * the requested parameters.
16163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
16263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public NetworkStatsHistory getHistory(
1631efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            NetworkTemplate template, int uid, int set, int tag, int fields,
1641efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            @NetworkStatsAccess.Level int accessLevel) {
1651efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson        return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
1661efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson                accessLevel);
16770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    }
16870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
16970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    /**
17070c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * Combine all {@link NetworkStatsHistory} in this collection which match
17170c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     * the requested parameters.
17270c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey     */
17370c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey    public NetworkStatsHistory getHistory(
1741efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
1751efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            @NetworkStatsAccess.Level int accessLevel) {
176cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado        return getHistory(template, uid, set, tag, fields, start, end, accessLevel,
177cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado                Binder.getCallingUid());
178cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    }
179cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado
180cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    /**
181cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     * Combine all {@link NetworkStatsHistory} in this collection which match
182cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     * the requested parameters.
183cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     */
184cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    public NetworkStatsHistory getHistory(
185cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado            NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
186cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado            @NetworkStatsAccess.Level int accessLevel, int callerUid) {
1871efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson        if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
1889c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban            throw new SecurityException("Network stats history of uid " + uid
1899c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                    + " is forbidden for caller " + callerUid);
1909c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        }
1919c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
19263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStatsHistory combined = new NetworkStatsHistory(
1939c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban                mBucketDuration, start == end ? 1 : estimateBuckets(), fields);
1949c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
1959c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        // shortcut when we know stats will be empty
1969c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        if (start == end) return combined;
1979c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
19855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
19955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
20098170b0db191f916bf2a0f104f5d5057c2aaeaa9Wenchao Tong            if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
20163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    && templateMatches(template, key.ident)) {
20255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                final NetworkStatsHistory value = mStats.valueAt(i);
20355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                combined.recordHistory(value, start, end);
20463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
20563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
20663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return combined;
20763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
20863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
20963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
21063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Summarize all {@link NetworkStatsHistory} in this collection which match
21163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * the requested parameters.
21263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
2131efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson    public NetworkStats getSummary(NetworkTemplate template, long start, long end,
2141efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            @NetworkStatsAccess.Level int accessLevel) {
215cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado        return getSummary(template, start, end, accessLevel, Binder.getCallingUid());
216cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    }
217cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado
218cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    /**
219cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     * Summarize all {@link NetworkStatsHistory} in this collection which match
220cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     * the requested parameters.
221cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado     */
222cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado    public NetworkStats getSummary(NetworkTemplate template, long start, long end,
223cd42acd9515bdce89d4f1401ee2888d684bf1918Antonio Cansado            @NetworkStatsAccess.Level int accessLevel, int callerUid) {
22463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final long now = System.currentTimeMillis();
22563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
22663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStats stats = new NetworkStats(end - start, 24);
22770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        // shortcut when we know stats will be empty
22870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        if (start == end) return stats;
22970c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey
2309c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        final NetworkStats.Entry entry = new NetworkStats.Entry();
2319c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban        NetworkStatsHistory.Entry historyEntry = null;
2329c5dfa5c79fff17f96bf977b86c0c9ceb8c3cf9bZoltan Szatmary-Ban
23355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
23455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
2351efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson            if (templateMatches(template, key.ident)
2361efb1335814aea8ee0696144ca0ab24159b86e54Jeff Davidson                    && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
23798170b0db191f916bf2a0f104f5d5057c2aaeaa9Wenchao Tong                    && key.set < NetworkStats.SET_DEBUG_START) {
23855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                final NetworkStatsHistory value = mStats.valueAt(i);
23955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                historyEntry = value.getValues(start, end, now, historyEntry);
24063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
24163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.iface = IFACE_ALL;
24263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.uid = key.uid;
24363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.set = key.set;
24463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.tag = key.tag;
2451f7e05eda687279ac9deb894f42ac927bd471ad2Jeff Davidson                entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
24663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.rxBytes = historyEntry.rxBytes;
24763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.rxPackets = historyEntry.rxPackets;
24863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.txBytes = historyEntry.txBytes;
24963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.txPackets = historyEntry.txPackets;
25063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                entry.operations = historyEntry.operations;
25163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
25263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                if (!entry.isEmpty()) {
25363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    stats.combineValues(entry);
25463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
25563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
25663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
25763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
25863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return stats;
25963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
26063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
26163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
262bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey     * Record given {@link android.net.NetworkStats.Entry} into this collection.
26363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
26463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
26563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            long end, NetworkStats.Entry entry) {
26670c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
26770c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        history.recordData(start, end, entry);
26870c70530bd6793869736ec894498e4ebf5dc9b20Jeff Sharkey        noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
26963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
27063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
27163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
27263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Record given {@link NetworkStatsHistory} into this collection.
27363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
27463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private void recordHistory(Key key, NetworkStatsHistory history) {
27563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (history.size() == 0) return;
27663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
27763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
278ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        NetworkStatsHistory target = mStats.get(key);
279ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        if (target == null) {
280ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey            target = new NetworkStatsHistory(history.getBucketDuration());
281ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey            mStats.put(key, target);
28263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
283ac3fcb1590e1da21324c13ce237ec48f2bf488bfJeff Sharkey        target.recordEntireHistory(history);
28463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
28563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
28663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
28763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Record all {@link NetworkStatsHistory} contained in the given collection
28863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * into this collection.
28963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
29063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void recordCollection(NetworkStatsCollection another) {
29155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < another.mStats.size(); i++) {
29255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = another.mStats.keyAt(i);
29355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = another.mStats.valueAt(i);
29455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            recordHistory(key, value);
29563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
29663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
29763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
29863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private NetworkStatsHistory findOrCreateHistory(
29963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            NetworkIdentitySet ident, int uid, int set, int tag) {
30063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final Key key = new Key(ident, uid, set, tag);
30163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final NetworkStatsHistory existing = mStats.get(key);
30263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
30363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // update when no existing, or when bucket duration changed
30463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        NetworkStatsHistory updated = null;
30563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (existing == null) {
30663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            updated = new NetworkStatsHistory(mBucketDuration, 10);
30763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } else if (existing.getBucketDuration() != mBucketDuration) {
30863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            updated = new NetworkStatsHistory(existing, mBucketDuration);
30963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
31063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
31163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (updated != null) {
31263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            mStats.put(key, updated);
31363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return updated;
31463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } else {
31563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return existing;
31663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
31763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
31863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
319bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey    @Override
32063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void read(InputStream in) throws IOException {
32163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        read(new DataInputStream(in));
32263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
32363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
32463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void read(DataInputStream in) throws IOException {
32563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // verify file magic header intact
32663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final int magic = in.readInt();
32763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (magic != FILE_MAGIC) {
32863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            throw new ProtocolException("unexpected magic: " + magic);
32963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
33063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
33163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final int version = in.readInt();
33263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        switch (version) {
33363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            case VERSION_UNIFIED_INIT: {
33463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
33563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final int identSize = in.readInt();
33663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                for (int i = 0; i < identSize; i++) {
33763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkIdentitySet ident = new NetworkIdentitySet(in);
33863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
33963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int size = in.readInt();
34063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int j = 0; j < size; j++) {
34163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int uid = in.readInt();
34263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int set = in.readInt();
34363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int tag = in.readInt();
34463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
34563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final Key key = new Key(ident, uid, set, tag);
34663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
34763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        recordHistory(key, history);
34863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
34963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
35063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                break;
35163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
35263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            default: {
35363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected version: " + version);
35463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
35563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
35663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
35763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
35863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void write(DataOutputStream out) throws IOException {
35963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // cluster key lists grouped by ident
36063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
36163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : mStats.keySet()) {
36263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            ArrayList<Key> keys = keysByIdent.get(key.ident);
36363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (keys == null) {
36463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                keys = Lists.newArrayList();
36563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                keysByIdent.put(key.ident, keys);
36663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
36763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            keys.add(key);
36863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
36963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(FILE_MAGIC);
37163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(VERSION_UNIFIED_INIT);
37263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.writeInt(keysByIdent.size());
37463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (NetworkIdentitySet ident : keysByIdent.keySet()) {
37563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final ArrayList<Key> keys = keysByIdent.get(ident);
37663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            ident.writeToStream(out);
37763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
37863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            out.writeInt(keys.size());
37963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            for (Key key : keys) {
38063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final NetworkStatsHistory history = mStats.get(key);
38163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.uid);
38263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.set);
38363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                out.writeInt(key.tag);
38463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                history.writeToStream(out);
38563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
38663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
38763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
38863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        out.flush();
38963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
39063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    @Deprecated
39263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void readLegacyNetwork(File file) throws IOException {
39363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final AtomicFile inputFile = new AtomicFile(file);
39463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        DataInputStream in = null;
39663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        try {
39763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
39863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
39963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // verify file magic header intact
40063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int magic = in.readInt();
40163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (magic != FILE_MAGIC) {
40263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected magic: " + magic);
40363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
40463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
40563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int version = in.readInt();
40663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            switch (version) {
40763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_NETWORK_INIT: {
40863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // network := size *(NetworkIdentitySet NetworkStatsHistory)
40963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int size = in.readInt();
41063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int i = 0; i < size; i++) {
41163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
41263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
41363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
41463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
41563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        recordHistory(key, history);
41663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
41763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
41863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
41963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                default: {
42063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    throw new ProtocolException("unexpected version: " + version);
42163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
42263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
42363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } catch (FileNotFoundException e) {
42463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // missing stats is okay, probably first boot
42563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } finally {
42663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            IoUtils.closeQuietly(in);
42763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
42863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
42963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
43063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    @Deprecated
43163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void readLegacyUid(File file, boolean onlyTags) throws IOException {
43263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final AtomicFile inputFile = new AtomicFile(file);
43363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
43463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        DataInputStream in = null;
43563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        try {
43663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
43763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
43863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // verify file magic header intact
43963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int magic = in.readInt();
44063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (magic != FILE_MAGIC) {
44163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                throw new ProtocolException("unexpected magic: " + magic);
44263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
44363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
44463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final int version = in.readInt();
44563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            switch (version) {
44663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_INIT: {
44763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(UID NetworkStatsHistory)
44863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
44963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // drop this data version, since we don't have a good
45063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // mapping into NetworkIdentitySet.
45163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
45263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
45363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_IDENT: {
45463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
45563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
45663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // drop this data version, since this version only existed
45763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // for a short time.
45863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
45963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
46063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_TAG:
46163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                case VERSION_UID_WITH_SET: {
46263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
46363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final int identSize = in.readInt();
46463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    for (int i = 0; i < identSize; i++) {
46563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
46663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
46763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        final int size = in.readInt();
46863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        for (int j = 0; j < size; j++) {
46963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int uid = in.readInt();
47063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
47163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                                    : SET_DEFAULT;
47263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final int tag = in.readInt();
47363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
47463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final Key key = new Key(ident, uid, set, tag);
47563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            final NetworkStatsHistory history = new NetworkStatsHistory(in);
47663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
47763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            if ((tag == TAG_NONE) != onlyTags) {
47863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                                recordHistory(key, history);
47963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            }
48063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                        }
48163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    }
48263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    break;
48363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
48463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                default: {
48563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    throw new ProtocolException("unexpected version: " + version);
48663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
48763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
48863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } catch (FileNotFoundException e) {
48963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            // missing stats is okay, probably first boot
49063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        } finally {
49163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            IoUtils.closeQuietly(in);
49263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
49363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
49463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
49563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
49663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
49763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * moving any {@link NetworkStats#TAG_NONE} series to
49863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * {@link TrafficStats#UID_REMOVED}.
49963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
500daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkey    public void removeUids(int[] uids) {
50163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final ArrayList<Key> knownKeys = Lists.newArrayList();
50263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        knownKeys.addAll(mStats.keySet());
50363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
50463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        // migrate all UID stats into special "removed" bucket
50563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : knownKeys) {
506daa57e8d1866fe4579c280c41604f3660db7cd01Jeff Sharkey            if (ArrayUtils.contains(uids, key.uid)) {
50763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                // only migrate combined TAG_NONE history
50863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                if (key.tag == TAG_NONE) {
50963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkStatsHistory uidHistory = mStats.get(key);
51063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    final NetworkStatsHistory removedHistory = findOrCreateHistory(
51163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                            key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
51263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                    removedHistory.recordEntireHistory(uidHistory);
51363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                }
51463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mStats.remove(key);
51563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                mDirty = true;
51663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
51763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
51863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
51963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
52063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
52163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (startMillis < mStartMillis) mStartMillis = startMillis;
52263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        if (endMillis > mEndMillis) mEndMillis = endMillis;
52363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mTotalBytes += totalBytes;
52463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        mDirty = true;
52563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
52663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
52763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private int estimateBuckets() {
52855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
52963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                / mBucketDuration);
53063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
53163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
53263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    public void dump(IndentingPrintWriter pw) {
53363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        final ArrayList<Key> keys = Lists.newArrayList();
53463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        keys.addAll(mStats.keySet());
53563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        Collections.sort(keys);
53663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
53763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (Key key : keys) {
53863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print("ident="); pw.print(key.ident.toString());
53963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" uid="); pw.print(key.uid);
54063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
54163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
54263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
54363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            final NetworkStatsHistory history = mStats.get(key);
54463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.increaseIndent();
54563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            history.dump(pw, true);
54663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            pw.decreaseIndent();
54763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
54863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
54963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
55055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    public void dumpCheckin(PrintWriter pw, long start, long end) {
55155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
55255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
55355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
55455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
55555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    }
55655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
55755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    /**
55855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     * Dump all contained stats that match requested parameters, but group
55955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     * together all matching {@link NetworkTemplate} under a single prefix.
56055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey     */
56155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
56255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            String groupPrefix) {
56355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
56455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
56555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        // Walk through all history, grouping by matching network templates
56655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < mStats.size(); i++) {
56755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = mStats.keyAt(i);
56855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = mStats.valueAt(i);
56955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
57055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (!templateMatches(groupTemplate, key.ident)) continue;
57198170b0db191f916bf2a0f104f5d5057c2aaeaa9Wenchao Tong            if (key.set >= NetworkStats.SET_DEBUG_START) continue;
57255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
57355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key groupKey = new Key(null, key.uid, key.set, key.tag);
57455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            NetworkStatsHistory groupHistory = grouped.get(groupKey);
57555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (groupHistory == null) {
57655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                groupHistory = new NetworkStatsHistory(value.getBucketDuration());
57755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                grouped.put(groupKey, groupHistory);
57855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
57955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            groupHistory.recordHistory(value, start, end);
58055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        }
58155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
58255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        for (int i = 0; i < grouped.size(); i++) {
58355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final Key key = grouped.keyAt(i);
58455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            final NetworkStatsHistory value = grouped.valueAt(i);
58555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
58655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (value.size() == 0) continue;
58755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
58855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print("c,");
58955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(groupPrefix); pw.print(',');
59055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(key.uid); pw.print(',');
59155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
59255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.print(key.tag);
59355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            pw.println();
59455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
59555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            value.dumpCheckin(pw);
59655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey        }
59755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey    }
59855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey
59963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    /**
60063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
60163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     * in the given {@link NetworkIdentitySet}.
60263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey     */
60363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
60463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        for (NetworkIdentity ident : identSet) {
60563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (template.matches(ident)) {
60663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                return true;
60763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
60863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
60963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        return false;
61063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
61163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
61263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    private static class Key implements Comparable<Key> {
61363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final NetworkIdentitySet ident;
61463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int uid;
61563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int set;
61663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public final int tag;
61763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
61863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        private final int hashCode;
61963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
62063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
62163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.ident = ident;
62263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.uid = uid;
62363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.set = set;
62463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            this.tag = tag;
625e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            hashCode = Objects.hash(ident, uid, set, tag);
62663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
62763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
62863abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        @Override
62963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public int hashCode() {
63063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return hashCode;
63163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
63263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
63363abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        @Override
63463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public boolean equals(Object obj) {
63563abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            if (obj instanceof Key) {
63663abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                final Key key = (Key) obj;
63763abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey                return uid == key.uid && set == key.set && tag == key.tag
638e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        && Objects.equals(ident, key.ident);
63963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            }
64063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey            return false;
64163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
64263abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey
643bfdd680ab44da173a4a39fcd6feccdebb9d1f855Jeff Sharkey        @Override
64463abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        public int compareTo(Key another) {
64555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            int res = 0;
64655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (ident != null && another.ident != null) {
64755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = ident.compareTo(another.ident);
64855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
64955a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
65055a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(uid, another.uid);
65155a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
65255a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
65355a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(set, another.set);
65455a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
65555a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            if (res == 0) {
65655a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey                res = Integer.compare(tag, another.tag);
65755a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            }
65855a442e58262e253df965d652bd8219c8d1e72d3Jeff Sharkey            return res;
65963abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey        }
66063abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey    }
66163abc37356728c0575d6a62a203102ae6d97953bJeff Sharkey}
662