NetworkStatsService.java revision 47eb102b40cd1324d89816a7fb0fecd14fd7a408
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.net;
18
19import static android.Manifest.permission.ACCESS_NETWORK_STATE;
20import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
21import static android.Manifest.permission.DUMP;
22import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
23import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
24import static android.content.Intent.ACTION_SHUTDOWN;
25import static android.content.Intent.ACTION_UID_REMOVED;
26import static android.content.Intent.EXTRA_UID;
27import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
28import static android.net.NetworkStats.IFACE_ALL;
29import static android.net.NetworkStats.SET_ALL;
30import static android.net.NetworkStats.SET_DEFAULT;
31import static android.net.NetworkStats.SET_FOREGROUND;
32import static android.net.NetworkStats.TAG_NONE;
33import static android.net.NetworkStats.UID_ALL;
34import static android.net.TrafficStats.UID_REMOVED;
35import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
36import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
37import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
38import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
39import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY;
40import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
41import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
42import static android.text.format.DateUtils.DAY_IN_MILLIS;
43import static android.text.format.DateUtils.HOUR_IN_MILLIS;
44import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
45import static com.android.internal.util.Preconditions.checkNotNull;
46import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
47import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
48import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
49
50import android.app.AlarmManager;
51import android.app.IAlarmManager;
52import android.app.PendingIntent;
53import android.content.BroadcastReceiver;
54import android.content.ContentResolver;
55import android.content.Context;
56import android.content.Intent;
57import android.content.IntentFilter;
58import android.content.pm.ApplicationInfo;
59import android.net.IConnectivityManager;
60import android.net.INetworkManagementEventObserver;
61import android.net.INetworkStatsService;
62import android.net.NetworkIdentity;
63import android.net.NetworkInfo;
64import android.net.NetworkState;
65import android.net.NetworkStats;
66import android.net.NetworkStatsHistory;
67import android.net.NetworkTemplate;
68import android.os.Binder;
69import android.os.Environment;
70import android.os.Handler;
71import android.os.HandlerThread;
72import android.os.INetworkManagementService;
73import android.os.Message;
74import android.os.PowerManager;
75import android.os.RemoteException;
76import android.os.SystemClock;
77import android.provider.Settings;
78import android.telephony.TelephonyManager;
79import android.util.NtpTrustedTime;
80import android.util.Slog;
81import android.util.SparseIntArray;
82import android.util.TrustedTime;
83
84import com.android.internal.os.AtomicFile;
85import com.android.internal.util.Objects;
86import com.google.android.collect.Lists;
87import com.google.android.collect.Maps;
88import com.google.android.collect.Sets;
89
90import java.io.BufferedInputStream;
91import java.io.BufferedOutputStream;
92import java.io.DataInputStream;
93import java.io.DataOutputStream;
94import java.io.File;
95import java.io.FileDescriptor;
96import java.io.FileNotFoundException;
97import java.io.FileOutputStream;
98import java.io.IOException;
99import java.io.PrintWriter;
100import java.net.ProtocolException;
101import java.util.ArrayList;
102import java.util.Collections;
103import java.util.HashMap;
104import java.util.HashSet;
105import java.util.List;
106
107import libcore.io.IoUtils;
108
109/**
110 * Collect and persist detailed network statistics, and provide this data to
111 * other system services.
112 */
113public class NetworkStatsService extends INetworkStatsService.Stub {
114    private static final String TAG = "NetworkStats";
115    private static final boolean LOGD = true;
116    private static final boolean LOGV = false;
117
118    /** File header magic number: "ANET" */
119    private static final int FILE_MAGIC = 0x414E4554;
120    private static final int VERSION_NETWORK_INIT = 1;
121    private static final int VERSION_UID_INIT = 1;
122    private static final int VERSION_UID_WITH_IDENT = 2;
123    private static final int VERSION_UID_WITH_TAG = 3;
124    private static final int VERSION_UID_WITH_SET = 4;
125
126    private static final int MSG_PERFORM_POLL = 0x1;
127    private static final int MSG_PERFORM_POLL_DETAILED = 0x2;
128
129    private final Context mContext;
130    private final INetworkManagementService mNetworkManager;
131    private final IAlarmManager mAlarmManager;
132    private final TrustedTime mTime;
133    private final NetworkStatsSettings mSettings;
134
135    private final PowerManager.WakeLock mWakeLock;
136
137    private IConnectivityManager mConnManager;
138
139    // @VisibleForTesting
140    public static final String ACTION_NETWORK_STATS_POLL =
141            "com.android.server.action.NETWORK_STATS_POLL";
142    public static final String ACTION_NETWORK_STATS_UPDATED =
143            "com.android.server.action.NETWORK_STATS_UPDATED";
144
145    private PendingIntent mPollIntent;
146
147    // TODO: trim empty history objects entirely
148
149    private static final long KB_IN_BYTES = 1024;
150    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
151    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
152
153    /**
154     * Settings that can be changed externally.
155     */
156    public interface NetworkStatsSettings {
157        public long getPollInterval();
158        public long getPersistThreshold();
159        public long getNetworkBucketDuration();
160        public long getNetworkMaxHistory();
161        public long getUidBucketDuration();
162        public long getUidMaxHistory();
163        public long getTagMaxHistory();
164        public long getTimeCacheMaxAge();
165    }
166
167    private final Object mStatsLock = new Object();
168
169    /** Set of currently active ifaces. */
170    private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
171    /** Set of historical network layer stats for known networks. */
172    private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
173    /** Set of historical network layer stats for known UIDs. */
174    private HashMap<UidStatsKey, NetworkStatsHistory> mUidStats = Maps.newHashMap();
175
176    /** Flag if {@link #mUidStats} have been loaded from disk. */
177    private boolean mUidStatsLoaded = false;
178
179    private NetworkStats mLastPollNetworkSnapshot;
180    private NetworkStats mLastPollUidSnapshot;
181    private NetworkStats mLastPollOperationsSnapshot;
182
183    private NetworkStats mLastPersistNetworkSnapshot;
184    private NetworkStats mLastPersistUidSnapshot;
185
186    /** Current counter sets for each UID. */
187    private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
188
189    /** Data layer operation counters for splicing into other structures. */
190    private NetworkStats mOperations = new NetworkStats(0L, 10);
191
192    private final HandlerThread mHandlerThread;
193    private final Handler mHandler;
194
195    private final AtomicFile mNetworkFile;
196    private final AtomicFile mUidFile;
197
198    public NetworkStatsService(
199            Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
200        this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context),
201                getSystemDir(), new DefaultNetworkStatsSettings(context));
202    }
203
204    private static File getSystemDir() {
205        return new File(Environment.getDataDirectory(), "system");
206    }
207
208    public NetworkStatsService(Context context, INetworkManagementService networkManager,
209            IAlarmManager alarmManager, TrustedTime time, File systemDir,
210            NetworkStatsSettings settings) {
211        mContext = checkNotNull(context, "missing Context");
212        mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
213        mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
214        mTime = checkNotNull(time, "missing TrustedTime");
215        mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
216
217        final PowerManager powerManager = (PowerManager) context.getSystemService(
218                Context.POWER_SERVICE);
219        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
220
221        mHandlerThread = new HandlerThread(TAG);
222        mHandlerThread.start();
223        mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
224
225        mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin"));
226        mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
227    }
228
229    public void bindConnectivityManager(IConnectivityManager connManager) {
230        mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
231    }
232
233    public void systemReady() {
234        synchronized (mStatsLock) {
235            // read historical network stats from disk, since policy service
236            // might need them right away. we delay loading detailed UID stats
237            // until actually needed.
238            readNetworkStatsLocked();
239        }
240
241        // watch for network interfaces to be claimed
242        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
243        mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
244
245        // listen for periodic polling events
246        final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
247        mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
248
249        // listen for uid removal to clean stats
250        final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
251        mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
252
253        // persist stats during clean shutdown
254        final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
255        mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
256
257        try {
258            mNetworkManager.registerObserver(mAlertObserver);
259        } catch (RemoteException e) {
260            // ouch, no push updates means we fall back to
261            // ACTION_NETWORK_STATS_POLL intervals.
262            Slog.e(TAG, "unable to register INetworkManagementEventObserver", e);
263        }
264
265        registerPollAlarmLocked();
266        registerGlobalAlert();
267
268        // bootstrap initial stats to prevent double-counting later
269        bootstrapStats();
270    }
271
272    private void shutdownLocked() {
273        mContext.unregisterReceiver(mConnReceiver);
274        mContext.unregisterReceiver(mPollReceiver);
275        mContext.unregisterReceiver(mRemovedReceiver);
276        mContext.unregisterReceiver(mShutdownReceiver);
277
278        writeNetworkStatsLocked();
279        if (mUidStatsLoaded) {
280            writeUidStatsLocked();
281        }
282        mNetworkStats.clear();
283        mUidStats.clear();
284        mUidStatsLoaded = false;
285    }
286
287    /**
288     * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
289     * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}.
290     */
291    private void registerPollAlarmLocked() {
292        try {
293            if (mPollIntent != null) {
294                mAlarmManager.remove(mPollIntent);
295            }
296
297            mPollIntent = PendingIntent.getBroadcast(
298                    mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
299
300            final long currentRealtime = SystemClock.elapsedRealtime();
301            mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
302                    mSettings.getPollInterval(), mPollIntent);
303        } catch (RemoteException e) {
304            Slog.w(TAG, "problem registering for poll alarm: " + e);
305        }
306    }
307
308    /**
309     * Register for a global alert that is delivered through
310     * {@link INetworkManagementEventObserver} once a threshold amount of data
311     * has been transferred.
312     */
313    private void registerGlobalAlert() {
314        try {
315            final long alertBytes = mSettings.getPersistThreshold();
316            mNetworkManager.setGlobalAlert(alertBytes);
317        } catch (IllegalStateException e) {
318            Slog.w(TAG, "problem registering for global alert: " + e);
319        } catch (RemoteException e) {
320            Slog.w(TAG, "problem registering for global alert: " + e);
321        }
322    }
323
324    @Override
325    public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
326        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
327
328        synchronized (mStatsLock) {
329            // combine all interfaces that match template
330            final NetworkStatsHistory combined = new NetworkStatsHistory(
331                    mSettings.getNetworkBucketDuration(), estimateNetworkBuckets(), fields);
332            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
333                if (templateMatches(template, ident)) {
334                    final NetworkStatsHistory history = mNetworkStats.get(ident);
335                    if (history != null) {
336                        combined.recordEntireHistory(history);
337                    }
338                }
339            }
340            return combined;
341        }
342    }
343
344    @Override
345    public NetworkStatsHistory getHistoryForUid(
346            NetworkTemplate template, int uid, int set, int tag, int fields) {
347        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
348
349        synchronized (mStatsLock) {
350            ensureUidStatsLoadedLocked();
351
352            // combine all interfaces that match template
353            final NetworkStatsHistory combined = new NetworkStatsHistory(
354                    mSettings.getUidBucketDuration(), estimateUidBuckets(), fields);
355            for (UidStatsKey key : mUidStats.keySet()) {
356                final boolean setMatches = set == SET_ALL || key.set == set;
357                if (templateMatches(template, key.ident) && key.uid == uid && setMatches
358                        && key.tag == tag) {
359                    final NetworkStatsHistory history = mUidStats.get(key);
360                    combined.recordEntireHistory(history);
361                }
362            }
363
364            return combined;
365        }
366    }
367
368    @Override
369    public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
370        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
371
372        synchronized (mStatsLock) {
373            // use system clock to be externally consistent
374            final long now = System.currentTimeMillis();
375
376            final NetworkStats stats = new NetworkStats(end - start, 1);
377            final NetworkStats.Entry entry = new NetworkStats.Entry();
378            NetworkStatsHistory.Entry historyEntry = null;
379
380            // combine total from all interfaces that match template
381            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
382                if (templateMatches(template, ident)) {
383                    final NetworkStatsHistory history = mNetworkStats.get(ident);
384                    historyEntry = history.getValues(start, end, now, historyEntry);
385
386                    entry.iface = IFACE_ALL;
387                    entry.uid = UID_ALL;
388                    entry.tag = TAG_NONE;
389                    entry.rxBytes = historyEntry.rxBytes;
390                    entry.txBytes = historyEntry.txBytes;
391
392                    stats.combineValues(entry);
393                }
394            }
395
396            return stats;
397        }
398    }
399
400    @Override
401    public NetworkStats getSummaryForAllUid(
402            NetworkTemplate template, long start, long end, boolean includeTags) {
403        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
404
405        synchronized (mStatsLock) {
406            ensureUidStatsLoadedLocked();
407
408            // use system clock to be externally consistent
409            final long now = System.currentTimeMillis();
410
411            final NetworkStats stats = new NetworkStats(end - start, 24);
412            final NetworkStats.Entry entry = new NetworkStats.Entry();
413            NetworkStatsHistory.Entry historyEntry = null;
414
415            for (UidStatsKey key : mUidStats.keySet()) {
416                if (templateMatches(template, key.ident)) {
417                    // always include summary under TAG_NONE, and include
418                    // other tags when requested.
419                    if (key.tag == TAG_NONE || includeTags) {
420                        final NetworkStatsHistory history = mUidStats.get(key);
421                        historyEntry = history.getValues(start, end, now, historyEntry);
422
423                        entry.iface = IFACE_ALL;
424                        entry.uid = key.uid;
425                        entry.set = key.set;
426                        entry.tag = key.tag;
427                        entry.rxBytes = historyEntry.rxBytes;
428                        entry.rxPackets = historyEntry.rxPackets;
429                        entry.txBytes = historyEntry.txBytes;
430                        entry.txPackets = historyEntry.txPackets;
431                        entry.operations = historyEntry.operations;
432
433                        if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
434                                || entry.txPackets > 0 || entry.operations > 0) {
435                            stats.combineValues(entry);
436                        }
437                    }
438                }
439            }
440
441            return stats;
442        }
443    }
444
445    @Override
446    public NetworkStats getDataLayerSnapshotForUid(int uid) throws RemoteException {
447        if (Binder.getCallingUid() != uid) {
448            mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
449        }
450
451        // TODO: switch to data layer stats once kernel exports
452        // for now, read network layer stats and flatten across all ifaces
453        final NetworkStats networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
454        final NetworkStats dataLayer = new NetworkStats(
455                networkLayer.getElapsedRealtime(), networkLayer.size());
456
457        NetworkStats.Entry entry = null;
458        for (int i = 0; i < networkLayer.size(); i++) {
459            entry = networkLayer.getValues(i, entry);
460            entry.iface = IFACE_ALL;
461            dataLayer.combineValues(entry);
462        }
463
464        // splice in operation counts
465        dataLayer.spliceOperationsFrom(mOperations);
466        return dataLayer;
467    }
468
469    @Override
470    public void incrementOperationCount(int uid, int tag, int operationCount) {
471        if (Binder.getCallingUid() != uid) {
472            mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
473        }
474
475        if (operationCount < 0) {
476            throw new IllegalArgumentException("operation count can only be incremented");
477        }
478        if (tag == TAG_NONE) {
479            throw new IllegalArgumentException("operation count must have specific tag");
480        }
481
482        synchronized (mStatsLock) {
483            final int set = mActiveUidCounterSet.get(uid, SET_DEFAULT);
484            mOperations.combineValues(IFACE_ALL, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
485            mOperations.combineValues(IFACE_ALL, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
486        }
487    }
488
489    @Override
490    public void setUidForeground(int uid, boolean uidForeground) {
491        mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
492
493        synchronized (mStatsLock) {
494            final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
495            final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
496            if (oldSet != set) {
497                mActiveUidCounterSet.put(uid, set);
498                setKernelCounterSet(uid, set);
499            }
500        }
501    }
502
503    @Override
504    public void forceUpdate() {
505        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
506        performPoll(true, false);
507    }
508
509    /**
510     * Receiver that watches for {@link IConnectivityManager} to claim network
511     * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
512     * with mobile interfaces.
513     */
514    private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
515        @Override
516        public void onReceive(Context context, Intent intent) {
517            // on background handler thread, and verified CONNECTIVITY_INTERNAL
518            // permission above.
519            synchronized (mStatsLock) {
520                mWakeLock.acquire();
521                try {
522                    updateIfacesLocked();
523                } finally {
524                    mWakeLock.release();
525                }
526            }
527        }
528    };
529
530    private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
531        @Override
532        public void onReceive(Context context, Intent intent) {
533            // on background handler thread, and verified UPDATE_DEVICE_STATS
534            // permission above.
535            performPoll(true, false);
536
537            // verify that we're watching global alert
538            registerGlobalAlert();
539        }
540    };
541
542    private BroadcastReceiver mRemovedReceiver = new BroadcastReceiver() {
543        @Override
544        public void onReceive(Context context, Intent intent) {
545            // on background handler thread, and UID_REMOVED is protected
546            // broadcast.
547            final int uid = intent.getIntExtra(EXTRA_UID, 0);
548            synchronized (mStatsLock) {
549                // TODO: perform one last stats poll for UID
550                mWakeLock.acquire();
551                try {
552                    removeUidLocked(uid);
553                } finally {
554                    mWakeLock.release();
555                }
556            }
557        }
558    };
559
560    private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
561        @Override
562        public void onReceive(Context context, Intent intent) {
563            // SHUTDOWN is protected broadcast.
564            synchronized (mStatsLock) {
565                shutdownLocked();
566            }
567        }
568    };
569
570    /**
571     * Observer that watches for {@link INetworkManagementService} alerts.
572     */
573    private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
574        @Override
575        public void limitReached(String limitName, String iface) {
576            // only someone like NMS should be calling us
577            mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
578
579            if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
580                // kick off background poll to collect network stats; UID stats
581                // are handled during normal polling interval.
582                mHandler.obtainMessage(MSG_PERFORM_POLL).sendToTarget();
583
584                // re-arm global alert for next update
585                registerGlobalAlert();
586            }
587        }
588    };
589
590    /**
591     * Inspect all current {@link NetworkState} to derive mapping from {@code
592     * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
593     * are active on a single {@code iface}, they are combined under a single
594     * {@link NetworkIdentitySet}.
595     */
596    private void updateIfacesLocked() {
597        if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
598
599        // take one last stats snapshot before updating iface mapping. this
600        // isn't perfect, since the kernel may already be counting traffic from
601        // the updated network.
602        performPollLocked(false, false);
603
604        final NetworkState[] states;
605        try {
606            states = mConnManager.getAllNetworkState();
607        } catch (RemoteException e) {
608            Slog.w(TAG, "problem reading network state");
609            return;
610        }
611
612        // rebuild active interfaces based on connected networks
613        mActiveIfaces.clear();
614
615        for (NetworkState state : states) {
616            if (state.networkInfo.isConnected()) {
617                // collect networks under their parent interfaces
618                final String iface = state.linkProperties.getInterfaceName();
619
620                NetworkIdentitySet ident = mActiveIfaces.get(iface);
621                if (ident == null) {
622                    ident = new NetworkIdentitySet();
623                    mActiveIfaces.put(iface, ident);
624                }
625
626                ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
627            }
628        }
629    }
630
631    /**
632     * Bootstrap initial stats snapshot, usually during {@link #systemReady()}
633     * so we have baseline values without double-counting.
634     */
635    private void bootstrapStats() {
636        try {
637            mLastPollNetworkSnapshot = mNetworkManager.getNetworkStatsSummary();
638            mLastPollUidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
639            mLastPollOperationsSnapshot = new NetworkStats(0L, 0);
640        } catch (IllegalStateException e) {
641            Slog.w(TAG, "problem reading network stats: " + e);
642        } catch (RemoteException e) {
643            Slog.w(TAG, "problem reading network stats: " + e);
644        }
645    }
646
647    private void performPoll(boolean detailedPoll, boolean forcePersist) {
648        synchronized (mStatsLock) {
649            mWakeLock.acquire();
650            try {
651                performPollLocked(detailedPoll, forcePersist);
652            } finally {
653                mWakeLock.release();
654            }
655        }
656    }
657
658    /**
659     * Periodic poll operation, reading current statistics and recording into
660     * {@link NetworkStatsHistory}.
661     *
662     * @param detailedPoll Indicate if detailed UID stats should be collected
663     *            during this poll operation.
664     */
665    private void performPollLocked(boolean detailedPoll, boolean forcePersist) {
666        if (LOGV) Slog.v(TAG, "performPollLocked()");
667        final long startRealtime = SystemClock.elapsedRealtime();
668
669        // try refreshing time source when stale
670        if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
671            mTime.forceRefresh();
672        }
673
674        // TODO: consider marking "untrusted" times in historical stats
675        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
676                : System.currentTimeMillis();
677        final long persistThreshold = mSettings.getPersistThreshold();
678
679        final NetworkStats networkSnapshot;
680        final NetworkStats uidSnapshot;
681        try {
682            networkSnapshot = mNetworkManager.getNetworkStatsSummary();
683            uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsUidDetail(UID_ALL) : null;
684        } catch (IllegalStateException e) {
685            Slog.w(TAG, "problem reading network stats: " + e);
686            return;
687        } catch (RemoteException e) {
688            Slog.w(TAG, "problem reading network stats: " + e);
689            return;
690        }
691
692        performNetworkPollLocked(networkSnapshot, currentTime);
693
694        // persist when enough network data has occurred
695        final NetworkStats persistNetworkDelta = computeStatsDelta(
696                mLastPersistNetworkSnapshot, networkSnapshot, true);
697        if (forcePersist || persistNetworkDelta.getTotalBytes() > persistThreshold) {
698            writeNetworkStatsLocked();
699            mLastPersistNetworkSnapshot = networkSnapshot;
700        }
701
702        if (detailedPoll) {
703            performUidPollLocked(uidSnapshot, currentTime);
704
705            // persist when enough network data has occurred
706            final NetworkStats persistUidDelta = computeStatsDelta(
707                    mLastPersistUidSnapshot, uidSnapshot, true);
708            if (forcePersist || persistUidDelta.getTotalBytes() > persistThreshold) {
709                writeUidStatsLocked();
710                mLastPersistUidSnapshot = networkSnapshot;
711            }
712        }
713
714        if (LOGV) {
715            final long duration = SystemClock.elapsedRealtime() - startRealtime;
716            Slog.v(TAG, "performPollLocked() took " + duration + "ms");
717        }
718
719        // finally, dispatch updated event to any listeners
720        final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
721        updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
722        mContext.sendBroadcast(updatedIntent, READ_NETWORK_USAGE_HISTORY);
723    }
724
725    /**
726     * Update {@link #mNetworkStats} historical usage.
727     */
728    private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) {
729        final HashSet<String> unknownIface = Sets.newHashSet();
730
731        final NetworkStats delta = computeStatsDelta(mLastPollNetworkSnapshot, networkSnapshot, false);
732        final long timeStart = currentTime - delta.getElapsedRealtime();
733
734        NetworkStats.Entry entry = null;
735        for (int i = 0; i < delta.size(); i++) {
736            entry = delta.getValues(i, entry);
737            final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
738            if (ident == null) {
739                unknownIface.add(entry.iface);
740                continue;
741            }
742
743            final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident);
744            history.recordData(timeStart, currentTime, entry);
745        }
746
747        // trim any history beyond max
748        final long maxHistory = mSettings.getNetworkMaxHistory();
749        for (NetworkStatsHistory history : mNetworkStats.values()) {
750            history.removeBucketsBefore(currentTime - maxHistory);
751        }
752
753        mLastPollNetworkSnapshot = networkSnapshot;
754
755        if (LOGD && unknownIface.size() > 0) {
756            Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
757        }
758    }
759
760    /**
761     * Update {@link #mUidStats} historical usage.
762     */
763    private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
764        ensureUidStatsLoadedLocked();
765
766        final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false);
767        final NetworkStats operationsDelta = computeStatsDelta(
768                mLastPollOperationsSnapshot, mOperations, false);
769        final long timeStart = currentTime - delta.getElapsedRealtime();
770
771        NetworkStats.Entry entry = null;
772        NetworkStats.Entry operationsEntry = null;
773        for (int i = 0; i < delta.size(); i++) {
774            entry = delta.getValues(i, entry);
775            final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
776            if (ident == null) {
777                continue;
778            }
779
780            // splice in operation counts since last poll
781            final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.set, entry.tag);
782            if (j != -1) {
783                operationsEntry = operationsDelta.getValues(j, operationsEntry);
784                entry.operations = operationsEntry.operations;
785            }
786
787            final NetworkStatsHistory history = findOrCreateUidStatsLocked(
788                    ident, entry.uid, entry.set, entry.tag);
789            history.recordData(timeStart, currentTime, entry);
790        }
791
792        // trim any history beyond max
793        final long maxUidHistory = mSettings.getUidMaxHistory();
794        final long maxTagHistory = mSettings.getTagMaxHistory();
795        for (UidStatsKey key : mUidStats.keySet()) {
796            final NetworkStatsHistory history = mUidStats.get(key);
797
798            // detailed tags are trimmed sooner than summary in TAG_NONE
799            if (key.tag == TAG_NONE) {
800                history.removeBucketsBefore(currentTime - maxUidHistory);
801            } else {
802                history.removeBucketsBefore(currentTime - maxTagHistory);
803            }
804        }
805
806        mLastPollUidSnapshot = uidSnapshot;
807        mLastPollOperationsSnapshot = mOperations;
808        mOperations = new NetworkStats(0L, 10);
809    }
810
811    /**
812     * Clean up {@link #mUidStats} after UID is removed.
813     */
814    private void removeUidLocked(int uid) {
815        ensureUidStatsLoadedLocked();
816
817        final ArrayList<UidStatsKey> knownKeys = Lists.newArrayList();
818        knownKeys.addAll(mUidStats.keySet());
819
820        // migrate all UID stats into special "removed" bucket
821        for (UidStatsKey key : knownKeys) {
822            if (key.uid == uid) {
823                // only migrate combined TAG_NONE history
824                if (key.tag == TAG_NONE) {
825                    final NetworkStatsHistory uidHistory = mUidStats.get(key);
826                    final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
827                            key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
828                    removedHistory.recordEntireHistory(uidHistory);
829                }
830                mUidStats.remove(key);
831            }
832        }
833
834        // clear kernel stats associated with UID
835        resetKernelUidStats(uid);
836
837        // since this was radical rewrite, push to disk
838        writeUidStatsLocked();
839    }
840
841    private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) {
842        final NetworkStatsHistory existing = mNetworkStats.get(ident);
843
844        // update when no existing, or when bucket duration changed
845        final long bucketDuration = mSettings.getNetworkBucketDuration();
846        NetworkStatsHistory updated = null;
847        if (existing == null) {
848            updated = new NetworkStatsHistory(bucketDuration, 10);
849        } else if (existing.getBucketDuration() != bucketDuration) {
850            updated = new NetworkStatsHistory(
851                    bucketDuration, estimateResizeBuckets(existing, bucketDuration));
852            updated.recordEntireHistory(existing);
853        }
854
855        if (updated != null) {
856            mNetworkStats.put(ident, updated);
857            return updated;
858        } else {
859            return existing;
860        }
861    }
862
863    private NetworkStatsHistory findOrCreateUidStatsLocked(
864            NetworkIdentitySet ident, int uid, int set, int tag) {
865        ensureUidStatsLoadedLocked();
866
867        final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
868        final NetworkStatsHistory existing = mUidStats.get(key);
869
870        // update when no existing, or when bucket duration changed
871        final long bucketDuration = mSettings.getUidBucketDuration();
872        NetworkStatsHistory updated = null;
873        if (existing == null) {
874            updated = new NetworkStatsHistory(bucketDuration, 10);
875        } else if (existing.getBucketDuration() != bucketDuration) {
876            updated = new NetworkStatsHistory(
877                    bucketDuration, estimateResizeBuckets(existing, bucketDuration));
878            updated.recordEntireHistory(existing);
879        }
880
881        if (updated != null) {
882            mUidStats.put(key, updated);
883            return updated;
884        } else {
885            return existing;
886        }
887    }
888
889    private void readNetworkStatsLocked() {
890        if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()");
891
892        // clear any existing stats and read from disk
893        mNetworkStats.clear();
894
895        DataInputStream in = null;
896        try {
897            in = new DataInputStream(new BufferedInputStream(mNetworkFile.openRead()));
898
899            // verify file magic header intact
900            final int magic = in.readInt();
901            if (magic != FILE_MAGIC) {
902                throw new ProtocolException("unexpected magic: " + magic);
903            }
904
905            final int version = in.readInt();
906            switch (version) {
907                case VERSION_NETWORK_INIT: {
908                    // network := size *(NetworkIdentitySet NetworkStatsHistory)
909                    final int size = in.readInt();
910                    for (int i = 0; i < size; i++) {
911                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
912                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
913                        mNetworkStats.put(ident, history);
914                    }
915                    break;
916                }
917                default: {
918                    throw new ProtocolException("unexpected version: " + version);
919                }
920            }
921        } catch (FileNotFoundException e) {
922            // missing stats is okay, probably first boot
923        } catch (IOException e) {
924            Slog.e(TAG, "problem reading network stats", e);
925        } finally {
926            IoUtils.closeQuietly(in);
927        }
928    }
929
930    private void ensureUidStatsLoadedLocked() {
931        if (!mUidStatsLoaded) {
932            readUidStatsLocked();
933            mUidStatsLoaded = true;
934        }
935    }
936
937    private void readUidStatsLocked() {
938        if (LOGV) Slog.v(TAG, "readUidStatsLocked()");
939
940        // clear any existing stats and read from disk
941        mUidStats.clear();
942
943        DataInputStream in = null;
944        try {
945            in = new DataInputStream(new BufferedInputStream(mUidFile.openRead()));
946
947            // verify file magic header intact
948            final int magic = in.readInt();
949            if (magic != FILE_MAGIC) {
950                throw new ProtocolException("unexpected magic: " + magic);
951            }
952
953            final int version = in.readInt();
954            switch (version) {
955                case VERSION_UID_INIT: {
956                    // uid := size *(UID NetworkStatsHistory)
957
958                    // drop this data version, since we don't have a good
959                    // mapping into NetworkIdentitySet.
960                    break;
961                }
962                case VERSION_UID_WITH_IDENT: {
963                    // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
964
965                    // drop this data version, since this version only existed
966                    // for a short time.
967                    break;
968                }
969                case VERSION_UID_WITH_TAG:
970                case VERSION_UID_WITH_SET: {
971                    // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
972                    final int identSize = in.readInt();
973                    for (int i = 0; i < identSize; i++) {
974                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
975
976                        final int size = in.readInt();
977                        for (int j = 0; j < size; j++) {
978                            final int uid = in.readInt();
979                            final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
980                                    : SET_DEFAULT;
981                            final int tag = in.readInt();
982
983                            final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
984                            final NetworkStatsHistory history = new NetworkStatsHistory(in);
985                            mUidStats.put(key, history);
986                        }
987                    }
988                    break;
989                }
990                default: {
991                    throw new ProtocolException("unexpected version: " + version);
992                }
993            }
994        } catch (FileNotFoundException e) {
995            // missing stats is okay, probably first boot
996        } catch (IOException e) {
997            Slog.e(TAG, "problem reading uid stats", e);
998        } finally {
999            IoUtils.closeQuietly(in);
1000        }
1001    }
1002
1003    private void writeNetworkStatsLocked() {
1004        if (LOGV) Slog.v(TAG, "writeNetworkStatsLocked()");
1005
1006        // TODO: consider duplicating stats and releasing lock while writing
1007
1008        FileOutputStream fos = null;
1009        try {
1010            fos = mNetworkFile.startWrite();
1011            final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
1012
1013            out.writeInt(FILE_MAGIC);
1014            out.writeInt(VERSION_NETWORK_INIT);
1015
1016            out.writeInt(mNetworkStats.size());
1017            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
1018                final NetworkStatsHistory history = mNetworkStats.get(ident);
1019                ident.writeToStream(out);
1020                history.writeToStream(out);
1021            }
1022
1023            out.flush();
1024            mNetworkFile.finishWrite(fos);
1025        } catch (IOException e) {
1026            Slog.w(TAG, "problem writing stats: ", e);
1027            if (fos != null) {
1028                mNetworkFile.failWrite(fos);
1029            }
1030        }
1031    }
1032
1033    private void writeUidStatsLocked() {
1034        if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
1035
1036        if (!mUidStatsLoaded) {
1037            Slog.w(TAG, "asked to write UID stats when not loaded; skipping");
1038            return;
1039        }
1040
1041        // TODO: consider duplicating stats and releasing lock while writing
1042
1043        // build UidStatsKey lists grouped by ident
1044        final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
1045        for (UidStatsKey key : mUidStats.keySet()) {
1046            ArrayList<UidStatsKey> keys = keysByIdent.get(key.ident);
1047            if (keys == null) {
1048                keys = Lists.newArrayList();
1049                keysByIdent.put(key.ident, keys);
1050            }
1051            keys.add(key);
1052        }
1053
1054        FileOutputStream fos = null;
1055        try {
1056            fos = mUidFile.startWrite();
1057            final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
1058
1059            out.writeInt(FILE_MAGIC);
1060            out.writeInt(VERSION_UID_WITH_SET);
1061
1062            out.writeInt(keysByIdent.size());
1063            for (NetworkIdentitySet ident : keysByIdent.keySet()) {
1064                final ArrayList<UidStatsKey> keys = keysByIdent.get(ident);
1065                ident.writeToStream(out);
1066
1067                out.writeInt(keys.size());
1068                for (UidStatsKey key : keys) {
1069                    final NetworkStatsHistory history = mUidStats.get(key);
1070                    out.writeInt(key.uid);
1071                    out.writeInt(key.set);
1072                    out.writeInt(key.tag);
1073                    history.writeToStream(out);
1074                }
1075            }
1076
1077            out.flush();
1078            mUidFile.finishWrite(fos);
1079        } catch (IOException e) {
1080            Slog.w(TAG, "problem writing stats: ", e);
1081            if (fos != null) {
1082                mUidFile.failWrite(fos);
1083            }
1084        }
1085    }
1086
1087    @Override
1088    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1089        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1090
1091        final HashSet<String> argSet = new HashSet<String>();
1092        for (String arg : args) {
1093            argSet.add(arg);
1094        }
1095
1096        final boolean fullHistory = argSet.contains("full");
1097
1098        synchronized (mStatsLock) {
1099            // TODO: remove this testing code, since it corrupts stats
1100            if (argSet.contains("generate")) {
1101                generateRandomLocked();
1102                pw.println("Generated stub stats");
1103                return;
1104            }
1105
1106            if (argSet.contains("poll")) {
1107                performPollLocked(true, true);
1108                pw.println("Forced poll");
1109                return;
1110            }
1111
1112            pw.println("Active interfaces:");
1113            for (String iface : mActiveIfaces.keySet()) {
1114                final NetworkIdentitySet ident = mActiveIfaces.get(iface);
1115                pw.print("  iface="); pw.print(iface);
1116                pw.print(" ident="); pw.println(ident.toString());
1117            }
1118
1119            pw.println("Known historical stats:");
1120            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
1121                final NetworkStatsHistory history = mNetworkStats.get(ident);
1122                pw.print("  ident="); pw.println(ident.toString());
1123                history.dump("  ", pw, fullHistory);
1124            }
1125
1126            if (argSet.contains("detail")) {
1127                // since explicitly requested with argument, we're okay to load
1128                // from disk if not already in memory.
1129                ensureUidStatsLoadedLocked();
1130
1131                final ArrayList<UidStatsKey> keys = Lists.newArrayList();
1132                keys.addAll(mUidStats.keySet());
1133                Collections.sort(keys);
1134
1135                pw.println("Detailed UID stats:");
1136                for (UidStatsKey key : keys) {
1137                    pw.print("  ident="); pw.print(key.ident.toString());
1138                    pw.print(" uid="); pw.print(key.uid);
1139                    pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
1140                    pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
1141
1142                    final NetworkStatsHistory history = mUidStats.get(key);
1143                    history.dump("    ", pw, fullHistory);
1144                }
1145            }
1146        }
1147    }
1148
1149    /**
1150     * @deprecated only for temporary testing
1151     */
1152    @Deprecated
1153    private void generateRandomLocked() {
1154        final long NET_END = System.currentTimeMillis();
1155        final long NET_START = NET_END - mSettings.getNetworkMaxHistory();
1156        final long NET_RX_BYTES = 3 * GB_IN_BYTES;
1157        final long NET_RX_PACKETS = NET_RX_BYTES / 1024;
1158        final long NET_TX_BYTES = 2 * GB_IN_BYTES;
1159        final long NET_TX_PACKETS = NET_TX_BYTES / 1024;
1160
1161        final long UID_END = System.currentTimeMillis();
1162        final long UID_START = UID_END - mSettings.getUidMaxHistory();
1163        final long UID_RX_BYTES = 500 * MB_IN_BYTES;
1164        final long UID_RX_PACKETS = UID_RX_BYTES / 1024;
1165        final long UID_TX_BYTES = 100 * MB_IN_BYTES;
1166        final long UID_TX_PACKETS = UID_TX_BYTES / 1024;
1167        final long UID_OPERATIONS = UID_RX_BYTES / 2048;
1168
1169        final List<ApplicationInfo> installedApps = mContext
1170                .getPackageManager().getInstalledApplications(0);
1171
1172        mNetworkStats.clear();
1173        mUidStats.clear();
1174        for (NetworkIdentitySet ident : mActiveIfaces.values()) {
1175            findOrCreateNetworkStatsLocked(ident).generateRandom(NET_START, NET_END, NET_RX_BYTES,
1176                    NET_RX_PACKETS, NET_TX_BYTES, NET_TX_PACKETS, 0L);
1177
1178            for (ApplicationInfo info : installedApps) {
1179                final int uid = info.uid;
1180                findOrCreateUidStatsLocked(ident, uid, SET_DEFAULT, TAG_NONE).generateRandom(
1181                        UID_START, UID_END, UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES,
1182                        UID_TX_PACKETS, UID_OPERATIONS);
1183                findOrCreateUidStatsLocked(ident, uid, SET_FOREGROUND, TAG_NONE).generateRandom(
1184                        UID_START, UID_END, UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES,
1185                        UID_TX_PACKETS, UID_OPERATIONS);
1186            }
1187        }
1188    }
1189
1190    /**
1191     * Return the delta between two {@link NetworkStats} snapshots, where {@code
1192     * before} can be {@code null}.
1193     */
1194    private static NetworkStats computeStatsDelta(
1195            NetworkStats before, NetworkStats current, boolean collectStale) {
1196        if (before != null) {
1197            return current.subtractClamped(before);
1198        } else if (collectStale) {
1199            // caller is okay collecting stale stats for first call.
1200            return current;
1201        } else {
1202            // this is first snapshot; to prevent from double-counting we only
1203            // observe traffic occuring between known snapshots.
1204            return new NetworkStats(0L, 10);
1205        }
1206    }
1207
1208    private int estimateNetworkBuckets() {
1209        return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
1210    }
1211
1212    private int estimateUidBuckets() {
1213        return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
1214    }
1215
1216    private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
1217        return (int) (existing.size() * existing.getBucketDuration() / newBucketDuration);
1218    }
1219
1220    /**
1221     * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
1222     * in the given {@link NetworkIdentitySet}.
1223     */
1224    private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
1225        for (NetworkIdentity ident : identSet) {
1226            if (template.matches(ident)) {
1227                return true;
1228            }
1229        }
1230        return false;
1231    }
1232
1233    private Handler.Callback mHandlerCallback = new Handler.Callback() {
1234        /** {@inheritDoc} */
1235        public boolean handleMessage(Message msg) {
1236            switch (msg.what) {
1237                case MSG_PERFORM_POLL: {
1238                    performPoll(false, false);
1239                    return true;
1240                }
1241                case MSG_PERFORM_POLL_DETAILED: {
1242                    performPoll(true, false);
1243                    return true;
1244                }
1245                default: {
1246                    return false;
1247                }
1248            }
1249        }
1250    };
1251
1252    /**
1253     * Key uniquely identifying a {@link NetworkStatsHistory} for a UID.
1254     */
1255    private static class UidStatsKey implements Comparable<UidStatsKey> {
1256        public final NetworkIdentitySet ident;
1257        public final int uid;
1258        public final int set;
1259        public final int tag;
1260
1261        public UidStatsKey(NetworkIdentitySet ident, int uid, int set, int tag) {
1262            this.ident = ident;
1263            this.uid = uid;
1264            this.set = set;
1265            this.tag = tag;
1266        }
1267
1268        @Override
1269        public int hashCode() {
1270            return Objects.hashCode(ident, uid, set, tag);
1271        }
1272
1273        @Override
1274        public boolean equals(Object obj) {
1275            if (obj instanceof UidStatsKey) {
1276                final UidStatsKey key = (UidStatsKey) obj;
1277                return Objects.equal(ident, key.ident) && uid == key.uid && set == key.set
1278                        && tag == key.tag;
1279            }
1280            return false;
1281        }
1282
1283        /** {@inheritDoc} */
1284        public int compareTo(UidStatsKey another) {
1285            return Integer.compare(uid, another.uid);
1286        }
1287    }
1288
1289    /**
1290     * Default external settings that read from {@link Settings.Secure}.
1291     */
1292    private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
1293        private final ContentResolver mResolver;
1294
1295        public DefaultNetworkStatsSettings(Context context) {
1296            mResolver = checkNotNull(context.getContentResolver());
1297            // TODO: adjust these timings for production builds
1298        }
1299
1300        private long getSecureLong(String name, long def) {
1301            return Settings.Secure.getLong(mResolver, name, def);
1302        }
1303
1304        public long getPollInterval() {
1305            return getSecureLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
1306        }
1307        public long getPersistThreshold() {
1308            return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 512 * KB_IN_BYTES);
1309        }
1310        public long getNetworkBucketDuration() {
1311            return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
1312        }
1313        public long getNetworkMaxHistory() {
1314            return getSecureLong(NETSTATS_NETWORK_MAX_HISTORY, 90 * DAY_IN_MILLIS);
1315        }
1316        public long getUidBucketDuration() {
1317            return getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
1318        }
1319        public long getUidMaxHistory() {
1320            return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS);
1321        }
1322        public long getTagMaxHistory() {
1323            return getSecureLong(NETSTATS_TAG_MAX_HISTORY, 30 * DAY_IN_MILLIS);
1324        }
1325        public long getTimeCacheMaxAge() {
1326            return DAY_IN_MILLIS;
1327        }
1328    }
1329}
1330