NetworkStatsService.java revision 0a9ee1272b59ad350cea591f931b52290a5e0998
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.CONNECTIVITY_INTERNAL;
20import static android.Manifest.permission.DUMP;
21import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
22import static android.content.Intent.ACTION_SHUTDOWN;
23import static android.content.Intent.ACTION_UID_REMOVED;
24import static android.content.Intent.EXTRA_UID;
25import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
26import static android.net.NetworkStats.IFACE_ALL;
27import static android.net.NetworkStats.TAG_NONE;
28import static android.net.NetworkStats.UID_ALL;
29import static android.net.TrafficStats.UID_REMOVED;
30import static android.provider.Settings.Secure.NETSTATS_ENABLED;
31import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
32import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
33import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
34import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
35import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY;
36import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
37import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
38import static android.text.format.DateUtils.DAY_IN_MILLIS;
39import static android.text.format.DateUtils.HOUR_IN_MILLIS;
40import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
41import static com.android.internal.util.Preconditions.checkNotNull;
42
43import android.app.AlarmManager;
44import android.app.IAlarmManager;
45import android.app.PendingIntent;
46import android.content.BroadcastReceiver;
47import android.content.ContentResolver;
48import android.content.Context;
49import android.content.Intent;
50import android.content.IntentFilter;
51import android.content.pm.ApplicationInfo;
52import android.net.IConnectivityManager;
53import android.net.INetworkStatsService;
54import android.net.NetworkIdentity;
55import android.net.NetworkInfo;
56import android.net.NetworkState;
57import android.net.NetworkStats;
58import android.net.NetworkStatsHistory;
59import android.net.NetworkTemplate;
60import android.os.Environment;
61import android.os.Handler;
62import android.os.HandlerThread;
63import android.os.INetworkManagementService;
64import android.os.RemoteException;
65import android.os.SystemClock;
66import android.provider.Settings;
67import android.telephony.TelephonyManager;
68import android.util.LongSparseArray;
69import android.util.NtpTrustedTime;
70import android.util.Slog;
71import android.util.TrustedTime;
72
73import com.android.internal.os.AtomicFile;
74import com.google.android.collect.Maps;
75import com.google.android.collect.Sets;
76
77import java.io.DataInputStream;
78import java.io.DataOutputStream;
79import java.io.File;
80import java.io.FileDescriptor;
81import java.io.FileInputStream;
82import java.io.FileNotFoundException;
83import java.io.FileOutputStream;
84import java.io.IOException;
85import java.io.PrintWriter;
86import java.net.ProtocolException;
87import java.util.HashMap;
88import java.util.HashSet;
89import java.util.List;
90
91import libcore.io.IoUtils;
92
93/**
94 * Collect and persist detailed network statistics, and provide this data to
95 * other system services.
96 */
97public class NetworkStatsService extends INetworkStatsService.Stub {
98    private static final String TAG = "NetworkStats";
99    private static final boolean LOGD = true;
100    private static final boolean LOGV = false;
101
102    /** File header magic number: "ANET" */
103    private static final int FILE_MAGIC = 0x414E4554;
104    private static final int VERSION_NETWORK_INIT = 1;
105    private static final int VERSION_UID_INIT = 1;
106    private static final int VERSION_UID_WITH_IDENT = 2;
107    private static final int VERSION_UID_WITH_TAG = 3;
108
109    private final Context mContext;
110    private final INetworkManagementService mNetworkManager;
111    private final IAlarmManager mAlarmManager;
112    private final TrustedTime mTime;
113    private final NetworkStatsSettings mSettings;
114
115    private IConnectivityManager mConnManager;
116
117    // @VisibleForTesting
118    public static final String ACTION_NETWORK_STATS_POLL =
119            "com.android.server.action.NETWORK_STATS_POLL";
120    public static final String ACTION_NETWORK_STATS_UPDATED =
121            "com.android.server.action.NETWORK_STATS_UPDATED";
122
123    private PendingIntent mPollIntent;
124
125    // TODO: listen for kernel push events through netd instead of polling
126    // TODO: watch for UID uninstall, and transfer stats into single bucket
127
128    // TODO: trim empty history objects entirely
129
130    private static final long KB_IN_BYTES = 1024;
131    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
132    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
133
134    /**
135     * Settings that can be changed externally.
136     */
137    public interface NetworkStatsSettings {
138        public boolean getEnabled();
139        public long getPollInterval();
140        public long getPersistThreshold();
141        public long getNetworkBucketDuration();
142        public long getNetworkMaxHistory();
143        public long getUidBucketDuration();
144        public long getUidMaxHistory();
145        public long getTagMaxHistory();
146        public long getTimeCacheMaxAge();
147    }
148
149    private final Object mStatsLock = new Object();
150
151    /** Set of currently active ifaces. */
152    private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
153    /** Set of historical stats for known networks. */
154    private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
155    /** Set of historical stats for known UIDs. */
156    private HashMap<NetworkIdentitySet, LongSparseArray<NetworkStatsHistory>> mUidStats =
157            Maps.newHashMap();
158
159    /** Flag if {@link #mUidStats} have been loaded from disk. */
160    private boolean mUidStatsLoaded = false;
161
162    private NetworkStats mLastNetworkSnapshot;
163    private NetworkStats mLastPersistNetworkSnapshot;
164
165    private NetworkStats mLastUidSnapshot;
166
167    private final HandlerThread mHandlerThread;
168    private final Handler mHandler;
169
170    private final AtomicFile mNetworkFile;
171    private final AtomicFile mUidFile;
172
173    // TODO: collect detailed uid stats, storing tag-granularity data until next
174    // dropbox, and uid summary for a specific bucket count.
175
176    // TODO: periodically compile statistics and send to dropbox.
177
178    public NetworkStatsService(
179            Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
180        // TODO: move to using cached NtpTrustedTime
181        this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir(),
182                new DefaultNetworkStatsSettings(context));
183    }
184
185    private static File getSystemDir() {
186        return new File(Environment.getDataDirectory(), "system");
187    }
188
189    public NetworkStatsService(Context context, INetworkManagementService networkManager,
190            IAlarmManager alarmManager, TrustedTime time, File systemDir,
191            NetworkStatsSettings settings) {
192        mContext = checkNotNull(context, "missing Context");
193        mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
194        mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
195        mTime = checkNotNull(time, "missing TrustedTime");
196        mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
197
198        mHandlerThread = new HandlerThread(TAG);
199        mHandlerThread.start();
200        mHandler = new Handler(mHandlerThread.getLooper());
201
202        mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin"));
203        mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
204    }
205
206    public void bindConnectivityManager(IConnectivityManager connManager) {
207        mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
208    }
209
210    public void systemReady() {
211        if (mSettings.getEnabled()) {
212            try {
213                // enable low-level bandwidth stats and control
214                // TODO: consider shipping with this enabled by default
215                mNetworkManager.setBandwidthControlEnabled(true);
216            } catch (RemoteException e) {
217                Slog.e(TAG, "problem enabling bandwidth controls", e);
218            }
219        } else {
220            Slog.w(TAG, "detailed network stats disabled");
221        }
222
223        synchronized (mStatsLock) {
224            // read historical network stats from disk, since policy service
225            // might need them right away. we delay loading detailed UID stats
226            // until actually needed.
227            readNetworkStatsLocked();
228        }
229
230        // watch for network interfaces to be claimed
231        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
232        mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
233
234        // listen for periodic polling events
235        final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
236        mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
237
238        // listen for uid removal to clean stats
239        final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
240        mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
241
242        // persist stats during clean shutdown
243        final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
244        mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
245
246        try {
247            registerPollAlarmLocked();
248        } catch (RemoteException e) {
249            Slog.w(TAG, "unable to register poll alarm");
250        }
251    }
252
253    private void shutdownLocked() {
254        mContext.unregisterReceiver(mConnReceiver);
255        mContext.unregisterReceiver(mPollReceiver);
256        mContext.unregisterReceiver(mRemovedReceiver);
257        mContext.unregisterReceiver(mShutdownReceiver);
258
259        writeNetworkStatsLocked();
260        if (mUidStatsLoaded) {
261            writeUidStatsLocked();
262        }
263        mNetworkStats.clear();
264        mUidStats.clear();
265        mUidStatsLoaded = false;
266    }
267
268    /**
269     * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
270     * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}.
271     */
272    private void registerPollAlarmLocked() throws RemoteException {
273        if (mPollIntent != null) {
274            mAlarmManager.remove(mPollIntent);
275        }
276
277        mPollIntent = PendingIntent.getBroadcast(
278                mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
279
280        final long currentRealtime = SystemClock.elapsedRealtime();
281        mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
282                mSettings.getPollInterval(), mPollIntent);
283    }
284
285    @Override
286    public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template) {
287        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
288
289        synchronized (mStatsLock) {
290            // combine all interfaces that match template
291            final NetworkStatsHistory combined = new NetworkStatsHistory(
292                    mSettings.getNetworkBucketDuration(), estimateNetworkBuckets());
293            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
294                if (templateMatches(template, ident)) {
295                    final NetworkStatsHistory history = mNetworkStats.get(ident);
296                    if (history != null) {
297                        combined.recordEntireHistory(history);
298                    }
299                }
300            }
301            return combined;
302        }
303    }
304
305    @Override
306    public NetworkStatsHistory getHistoryForUid(NetworkTemplate template, int uid, int tag) {
307        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
308
309        synchronized (mStatsLock) {
310            ensureUidStatsLoadedLocked();
311            final long packed = packUidAndTag(uid, tag);
312
313            // combine all interfaces that match template
314            final NetworkStatsHistory combined = new NetworkStatsHistory(
315                    mSettings.getUidBucketDuration(), estimateUidBuckets());
316            for (NetworkIdentitySet ident : mUidStats.keySet()) {
317                if (templateMatches(template, ident)) {
318                    final NetworkStatsHistory history = mUidStats.get(ident).get(packed);
319                    if (history != null) {
320                        combined.recordEntireHistory(history);
321                    }
322                }
323            }
324            return combined;
325        }
326    }
327
328    @Override
329    public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
330        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
331
332        synchronized (mStatsLock) {
333            long rx = 0;
334            long tx = 0;
335            long[] networkTotal = new long[2];
336
337            // combine total from all interfaces that match template
338            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
339                if (templateMatches(template, ident)) {
340                    final NetworkStatsHistory history = mNetworkStats.get(ident);
341                    networkTotal = history.getTotalData(start, end, networkTotal);
342                    rx += networkTotal[0];
343                    tx += networkTotal[1];
344                }
345            }
346
347            final NetworkStats stats = new NetworkStats(end - start, 1);
348            stats.addEntry(IFACE_ALL, UID_ALL, TAG_NONE, rx, tx);
349            return stats;
350        }
351    }
352
353    @Override
354    public NetworkStats getSummaryForAllUid(
355            NetworkTemplate template, long start, long end, boolean includeTags) {
356        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
357
358        synchronized (mStatsLock) {
359            ensureUidStatsLoadedLocked();
360
361            final NetworkStats stats = new NetworkStats(end - start, 24);
362            long[] total = new long[2];
363
364            for (NetworkIdentitySet ident : mUidStats.keySet()) {
365                if (templateMatches(template, ident)) {
366                    final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
367                    for (int i = 0; i < uidStats.size(); i++) {
368                        final long packed = uidStats.keyAt(i);
369                        final int uid = unpackUid(packed);
370                        final int tag = unpackTag(packed);
371
372                        // always include summary under TAG_NONE, and include
373                        // other tags when requested.
374                        if (tag == TAG_NONE || includeTags) {
375                            final NetworkStatsHistory history = uidStats.valueAt(i);
376                            total = history.getTotalData(start, end, total);
377                            final long rx = total[0];
378                            final long tx = total[1];
379                            if (rx > 0 || tx > 0) {
380                                stats.combineEntry(IFACE_ALL, uid, tag, rx, tx);
381                            }
382                        }
383                    }
384                }
385            }
386
387            return stats;
388        }
389    }
390
391    /**
392     * Receiver that watches for {@link IConnectivityManager} to claim network
393     * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
394     * with mobile interfaces.
395     */
396    private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
397        @Override
398        public void onReceive(Context context, Intent intent) {
399            // on background handler thread, and verified CONNECTIVITY_INTERNAL
400            // permission above.
401            synchronized (mStatsLock) {
402                updateIfacesLocked();
403            }
404        }
405    };
406
407    private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
408        @Override
409        public void onReceive(Context context, Intent intent) {
410            // on background handler thread, and verified UPDATE_DEVICE_STATS
411            // permission above.
412            synchronized (mStatsLock) {
413                // TODO: acquire wakelock while performing poll
414                performPollLocked(true, false);
415            }
416        }
417    };
418
419    private BroadcastReceiver mRemovedReceiver = new BroadcastReceiver() {
420        @Override
421        public void onReceive(Context context, Intent intent) {
422            // on background handler thread, and UID_REMOVED is protected
423            // broadcast.
424            final int uid = intent.getIntExtra(EXTRA_UID, 0);
425            synchronized (mStatsLock) {
426                removeUidLocked(uid);
427            }
428        }
429    };
430
431    private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
432        @Override
433        public void onReceive(Context context, Intent intent) {
434            // SHUTDOWN is protected broadcast.
435            synchronized (mStatsLock) {
436                shutdownLocked();
437            }
438        }
439    };
440
441    /**
442     * Inspect all current {@link NetworkState} to derive mapping from {@code
443     * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
444     * are active on a single {@code iface}, they are combined under a single
445     * {@link NetworkIdentitySet}.
446     */
447    private void updateIfacesLocked() {
448        if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
449
450        // take one last stats snapshot before updating iface mapping. this
451        // isn't perfect, since the kernel may already be counting traffic from
452        // the updated network.
453        performPollLocked(false, false);
454
455        final NetworkState[] states;
456        try {
457            states = mConnManager.getAllNetworkState();
458        } catch (RemoteException e) {
459            Slog.w(TAG, "problem reading network state");
460            return;
461        }
462
463        // rebuild active interfaces based on connected networks
464        mActiveIfaces.clear();
465
466        for (NetworkState state : states) {
467            if (state.networkInfo.isConnected()) {
468                // collect networks under their parent interfaces
469                final String iface = state.linkProperties.getInterfaceName();
470
471                NetworkIdentitySet ident = mActiveIfaces.get(iface);
472                if (ident == null) {
473                    ident = new NetworkIdentitySet();
474                    mActiveIfaces.put(iface, ident);
475                }
476
477                ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
478            }
479        }
480    }
481
482    /**
483     * Periodic poll operation, reading current statistics and recording into
484     * {@link NetworkStatsHistory}.
485     *
486     * @param detailedPoll Indicate if detailed UID stats should be collected
487     *            during this poll operation.
488     */
489    private void performPollLocked(boolean detailedPoll, boolean forcePersist) {
490        if (LOGV) Slog.v(TAG, "performPollLocked()");
491
492        // try refreshing time source when stale
493        if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
494            mTime.forceRefresh();
495        }
496
497        // TODO: consider marking "untrusted" times in historical stats
498        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
499                : System.currentTimeMillis();
500
501        final NetworkStats networkSnapshot;
502        final NetworkStats uidSnapshot;
503        try {
504            networkSnapshot = mNetworkManager.getNetworkStatsSummary();
505            uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
506        } catch (RemoteException e) {
507            Slog.w(TAG, "problem reading network stats");
508            return;
509        }
510
511        performNetworkPollLocked(networkSnapshot, currentTime);
512        if (detailedPoll) {
513            performUidPollLocked(uidSnapshot, currentTime);
514        }
515
516        // decide if enough has changed to trigger persist
517        final NetworkStats persistDelta = computeStatsDelta(
518                mLastPersistNetworkSnapshot, networkSnapshot);
519        final long persistThreshold = mSettings.getPersistThreshold();
520        for (String iface : persistDelta.getUniqueIfaces()) {
521            final int index = persistDelta.findIndex(iface, UID_ALL, TAG_NONE);
522            if (forcePersist || persistDelta.rx[index] > persistThreshold
523                    || persistDelta.tx[index] > persistThreshold) {
524                writeNetworkStatsLocked();
525                if (mUidStatsLoaded) {
526                    writeUidStatsLocked();
527                }
528                mLastPersistNetworkSnapshot = networkSnapshot;
529                break;
530            }
531        }
532
533        // finally, dispatch updated event to any listeners
534        final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
535        updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
536        mContext.sendBroadcast(updatedIntent, READ_NETWORK_USAGE_HISTORY);
537    }
538
539    /**
540     * Update {@link #mNetworkStats} historical usage.
541     */
542    private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) {
543        final HashSet<String> unknownIface = Sets.newHashSet();
544
545        final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot);
546        final long timeStart = currentTime - delta.elapsedRealtime;
547        for (int i = 0; i < delta.size; i++) {
548            final String iface = delta.iface[i];
549            final NetworkIdentitySet ident = mActiveIfaces.get(iface);
550            if (ident == null) {
551                unknownIface.add(iface);
552                continue;
553            }
554
555            final long rx = delta.rx[i];
556            final long tx = delta.tx[i];
557
558            final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident);
559            history.recordData(timeStart, currentTime, rx, tx);
560        }
561
562        // trim any history beyond max
563        final long maxHistory = mSettings.getNetworkMaxHistory();
564        for (NetworkStatsHistory history : mNetworkStats.values()) {
565            history.removeBucketsBefore(currentTime - maxHistory);
566        }
567
568        mLastNetworkSnapshot = networkSnapshot;
569
570        if (LOGD && unknownIface.size() > 0) {
571            Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
572        }
573    }
574
575    /**
576     * Update {@link #mUidStats} historical usage.
577     */
578    private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
579        ensureUidStatsLoadedLocked();
580
581        final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot);
582        final long timeStart = currentTime - delta.elapsedRealtime;
583
584        for (int i = 0; i < delta.size; i++) {
585            final String iface = delta.iface[i];
586            final NetworkIdentitySet ident = mActiveIfaces.get(iface);
587            if (ident == null) {
588                continue;
589            }
590
591            final int uid = delta.uid[i];
592            final int tag = delta.tag[i];
593            final long rx = delta.rx[i];
594            final long tx = delta.tx[i];
595
596            final NetworkStatsHistory history = findOrCreateUidStatsLocked(ident, uid, tag);
597            history.recordData(timeStart, currentTime, rx, tx);
598        }
599
600        // trim any history beyond max
601        final long maxUidHistory = mSettings.getUidMaxHistory();
602        final long maxTagHistory = mSettings.getTagMaxHistory();
603        for (LongSparseArray<NetworkStatsHistory> uidStats : mUidStats.values()) {
604            for (int i = 0; i < uidStats.size(); i++) {
605                final long packed = uidStats.keyAt(i);
606                final NetworkStatsHistory history = uidStats.valueAt(i);
607
608                // detailed tags are trimmed sooner than summary in TAG_NONE
609                if (unpackTag(packed) == TAG_NONE) {
610                    history.removeBucketsBefore(currentTime - maxUidHistory);
611                } else {
612                    history.removeBucketsBefore(currentTime - maxTagHistory);
613                }
614            }
615        }
616
617        mLastUidSnapshot = uidSnapshot;
618    }
619
620    /**
621     * Clean up {@link #mUidStats} after UID is removed.
622     */
623    private void removeUidLocked(int uid) {
624        ensureUidStatsLoadedLocked();
625
626        // migrate all UID stats into special "removed" bucket
627        for (NetworkIdentitySet ident : mUidStats.keySet()) {
628            final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
629            for (int i = 0; i < uidStats.size(); i++) {
630                final long packed = uidStats.keyAt(i);
631                if (unpackUid(packed) == uid) {
632                    // only migrate combined TAG_NONE history
633                    if (unpackTag(packed) == TAG_NONE) {
634                        final NetworkStatsHistory uidHistory = uidStats.valueAt(i);
635                        final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
636                                ident, UID_REMOVED, TAG_NONE);
637                        removedHistory.recordEntireHistory(uidHistory);
638                    }
639                    uidStats.remove(packed);
640                }
641            }
642        }
643
644        // TODO: push kernel event to wipe stats for UID, otherwise we risk
645        // picking them up again during next poll.
646
647        // since this was radical rewrite, push to disk
648        writeUidStatsLocked();
649    }
650
651    private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) {
652        final NetworkStatsHistory existing = mNetworkStats.get(ident);
653
654        // update when no existing, or when bucket duration changed
655        final long bucketDuration = mSettings.getNetworkBucketDuration();
656        NetworkStatsHistory updated = null;
657        if (existing == null) {
658            updated = new NetworkStatsHistory(bucketDuration, 10);
659        } else if (existing.bucketDuration != bucketDuration) {
660            updated = new NetworkStatsHistory(
661                    bucketDuration, estimateResizeBuckets(existing, bucketDuration));
662            updated.recordEntireHistory(existing);
663        }
664
665        if (updated != null) {
666            mNetworkStats.put(ident, updated);
667            return updated;
668        } else {
669            return existing;
670        }
671    }
672
673    private NetworkStatsHistory findOrCreateUidStatsLocked(
674            NetworkIdentitySet ident, int uid, int tag) {
675        ensureUidStatsLoadedLocked();
676
677        LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
678        if (uidStats == null) {
679            uidStats = new LongSparseArray<NetworkStatsHistory>();
680            mUidStats.put(ident, uidStats);
681        }
682
683        final long packed = packUidAndTag(uid, tag);
684        final NetworkStatsHistory existing = uidStats.get(packed);
685
686        // update when no existing, or when bucket duration changed
687        final long bucketDuration = mSettings.getUidBucketDuration();
688        NetworkStatsHistory updated = null;
689        if (existing == null) {
690            updated = new NetworkStatsHistory(bucketDuration, 10);
691        } else if (existing.bucketDuration != bucketDuration) {
692            updated = new NetworkStatsHistory(
693                    bucketDuration, estimateResizeBuckets(existing, bucketDuration));
694            updated.recordEntireHistory(existing);
695        }
696
697        if (updated != null) {
698            uidStats.put(packed, updated);
699            return updated;
700        } else {
701            return existing;
702        }
703    }
704
705    private void readNetworkStatsLocked() {
706        if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()");
707
708        // clear any existing stats and read from disk
709        mNetworkStats.clear();
710
711        FileInputStream fis = null;
712        try {
713            fis = mNetworkFile.openRead();
714            final DataInputStream in = new DataInputStream(fis);
715
716            // verify file magic header intact
717            final int magic = in.readInt();
718            if (magic != FILE_MAGIC) {
719                throw new ProtocolException("unexpected magic: " + magic);
720            }
721
722            final int version = in.readInt();
723            switch (version) {
724                case VERSION_NETWORK_INIT: {
725                    // network := size *(NetworkIdentitySet NetworkStatsHistory)
726                    final int size = in.readInt();
727                    for (int i = 0; i < size; i++) {
728                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
729                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
730                        mNetworkStats.put(ident, history);
731                    }
732                    break;
733                }
734                default: {
735                    throw new ProtocolException("unexpected version: " + version);
736                }
737            }
738        } catch (FileNotFoundException e) {
739            // missing stats is okay, probably first boot
740        } catch (IOException e) {
741            Slog.e(TAG, "problem reading network stats", e);
742        } finally {
743            IoUtils.closeQuietly(fis);
744        }
745    }
746
747    private void ensureUidStatsLoadedLocked() {
748        if (!mUidStatsLoaded) {
749            readUidStatsLocked();
750            mUidStatsLoaded = true;
751        }
752    }
753
754    private void readUidStatsLocked() {
755        if (LOGV) Slog.v(TAG, "readUidStatsLocked()");
756
757        // clear any existing stats and read from disk
758        mUidStats.clear();
759
760        FileInputStream fis = null;
761        try {
762            fis = mUidFile.openRead();
763            final DataInputStream in = new DataInputStream(fis);
764
765            // verify file magic header intact
766            final int magic = in.readInt();
767            if (magic != FILE_MAGIC) {
768                throw new ProtocolException("unexpected magic: " + magic);
769            }
770
771            final int version = in.readInt();
772            switch (version) {
773                case VERSION_UID_INIT: {
774                    // uid := size *(UID NetworkStatsHistory)
775
776                    // drop this data version, since we don't have a good
777                    // mapping into NetworkIdentitySet.
778                    break;
779                }
780                case VERSION_UID_WITH_IDENT: {
781                    // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
782
783                    // drop this data version, since this version only existed
784                    // for a short time.
785                    break;
786                }
787                case VERSION_UID_WITH_TAG: {
788                    // uid := size *(NetworkIdentitySet size *(UID tag NetworkStatsHistory))
789                    final int ifaceSize = in.readInt();
790                    for (int i = 0; i < ifaceSize; i++) {
791                        final NetworkIdentitySet ident = new NetworkIdentitySet(in);
792
793                        final int childSize = in.readInt();
794                        final LongSparseArray<NetworkStatsHistory> uidStats = new LongSparseArray<
795                                NetworkStatsHistory>(childSize);
796                        for (int j = 0; j < childSize; j++) {
797                            final int uid = in.readInt();
798                            final int tag = in.readInt();
799                            final long packed = packUidAndTag(uid, tag);
800
801                            final NetworkStatsHistory history = new NetworkStatsHistory(in);
802                            uidStats.put(packed, history);
803                        }
804
805                        mUidStats.put(ident, uidStats);
806                    }
807                    break;
808                }
809                default: {
810                    throw new ProtocolException("unexpected version: " + version);
811                }
812            }
813        } catch (FileNotFoundException e) {
814            // missing stats is okay, probably first boot
815        } catch (IOException e) {
816            Slog.e(TAG, "problem reading uid stats", e);
817        } finally {
818            IoUtils.closeQuietly(fis);
819        }
820    }
821
822    private void writeNetworkStatsLocked() {
823        if (LOGV) Slog.v(TAG, "writeNetworkStatsLocked()");
824
825        // TODO: consider duplicating stats and releasing lock while writing
826
827        FileOutputStream fos = null;
828        try {
829            fos = mNetworkFile.startWrite();
830            final DataOutputStream out = new DataOutputStream(fos);
831
832            out.writeInt(FILE_MAGIC);
833            out.writeInt(VERSION_NETWORK_INIT);
834
835            out.writeInt(mNetworkStats.size());
836            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
837                final NetworkStatsHistory history = mNetworkStats.get(ident);
838                ident.writeToStream(out);
839                history.writeToStream(out);
840            }
841
842            mNetworkFile.finishWrite(fos);
843        } catch (IOException e) {
844            if (fos != null) {
845                mNetworkFile.failWrite(fos);
846            }
847        }
848    }
849
850    private void writeUidStatsLocked() {
851        if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
852
853        if (!mUidStatsLoaded) {
854            Slog.w(TAG, "asked to write UID stats when not loaded; skipping");
855            return;
856        }
857
858        // TODO: consider duplicating stats and releasing lock while writing
859
860        FileOutputStream fos = null;
861        try {
862            fos = mUidFile.startWrite();
863            final DataOutputStream out = new DataOutputStream(fos);
864
865            out.writeInt(FILE_MAGIC);
866            out.writeInt(VERSION_UID_WITH_TAG);
867
868            final int size = mUidStats.size();
869            out.writeInt(size);
870            for (NetworkIdentitySet ident : mUidStats.keySet()) {
871                final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
872                ident.writeToStream(out);
873
874                final int childSize = uidStats.size();
875                out.writeInt(childSize);
876                for (int i = 0; i < childSize; i++) {
877                    final long packed = uidStats.keyAt(i);
878                    final int uid = unpackUid(packed);
879                    final int tag = unpackTag(packed);
880                    final NetworkStatsHistory history = uidStats.valueAt(i);
881                    out.writeInt(uid);
882                    out.writeInt(tag);
883                    history.writeToStream(out);
884                }
885            }
886
887            mUidFile.finishWrite(fos);
888        } catch (IOException e) {
889            if (fos != null) {
890                mUidFile.failWrite(fos);
891            }
892        }
893    }
894
895    @Override
896    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
897        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
898
899        final HashSet<String> argSet = new HashSet<String>();
900        for (String arg : args) {
901            argSet.add(arg);
902        }
903
904        synchronized (mStatsLock) {
905            // TODO: remove this testing code, since it corrupts stats
906            if (argSet.contains("generate")) {
907                generateRandomLocked();
908                pw.println("Generated stub stats");
909                return;
910            }
911
912            if (argSet.contains("poll")) {
913                performPollLocked(true, true);
914                pw.println("Forced poll");
915                return;
916            }
917
918            pw.println("Active interfaces:");
919            for (String iface : mActiveIfaces.keySet()) {
920                final NetworkIdentitySet ident = mActiveIfaces.get(iface);
921                pw.print("  iface="); pw.print(iface);
922                pw.print(" ident="); pw.println(ident.toString());
923            }
924
925            pw.println("Known historical stats:");
926            for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
927                final NetworkStatsHistory history = mNetworkStats.get(ident);
928                pw.print("  ident="); pw.println(ident.toString());
929                history.dump("  ", pw);
930            }
931
932            if (argSet.contains("detail")) {
933                // since explicitly requested with argument, we're okay to load
934                // from disk if not already in memory.
935                ensureUidStatsLoadedLocked();
936
937                pw.println("Detailed UID stats:");
938                for (NetworkIdentitySet ident : mUidStats.keySet()) {
939                    pw.print("  ident="); pw.println(ident.toString());
940
941                    final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
942                    for (int i = 0; i < uidStats.size(); i++) {
943                        final long packed = uidStats.keyAt(i);
944                        final int uid = unpackUid(packed);
945                        final int tag = unpackTag(packed);
946                        final NetworkStatsHistory history = uidStats.valueAt(i);
947                        pw.print("    UID="); pw.print(uid);
948                        pw.print(" tag="); pw.println(tag);
949                        history.dump("    ", pw);
950                    }
951                }
952            }
953        }
954    }
955
956    /**
957     * @deprecated only for temporary testing
958     */
959    @Deprecated
960    private void generateRandomLocked() {
961        long networkEnd = System.currentTimeMillis();
962        long networkStart = networkEnd - mSettings.getNetworkMaxHistory();
963        long networkRx = 3 * GB_IN_BYTES;
964        long networkTx = 2 * GB_IN_BYTES;
965
966        long uidEnd = System.currentTimeMillis();
967        long uidStart = uidEnd - mSettings.getUidMaxHistory();
968        long uidRx = 500 * MB_IN_BYTES;
969        long uidTx = 100 * MB_IN_BYTES;
970
971        final List<ApplicationInfo> installedApps = mContext
972                .getPackageManager().getInstalledApplications(0);
973
974        mNetworkStats.clear();
975        mUidStats.clear();
976        for (NetworkIdentitySet ident : mActiveIfaces.values()) {
977            findOrCreateNetworkStatsLocked(ident).generateRandom(
978                    networkStart, networkEnd, networkRx, networkTx);
979
980            for (ApplicationInfo info : installedApps) {
981                final int uid = info.uid;
982                findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom(
983                        uidStart, uidEnd, uidRx, uidTx);
984            }
985        }
986    }
987
988    /**
989     * Return the delta between two {@link NetworkStats} snapshots, where {@code
990     * before} can be {@code null}.
991     */
992    private static NetworkStats computeStatsDelta(NetworkStats before, NetworkStats current) {
993        if (before != null) {
994            return current.subtractClamped(before);
995        } else {
996            return current;
997        }
998    }
999
1000    private int estimateNetworkBuckets() {
1001        return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
1002    }
1003
1004    private int estimateUidBuckets() {
1005        return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
1006    }
1007
1008    private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
1009        return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration);
1010    }
1011
1012    // @VisibleForTesting
1013    public static long packUidAndTag(int uid, int tag) {
1014        final long uidLong = uid;
1015        final long tagLong = tag;
1016        return (uidLong << 32) | (tagLong & 0xFFFFFFFFL);
1017    }
1018
1019    // @VisibleForTesting
1020    public static int unpackUid(long packed) {
1021        return (int) (packed >> 32);
1022    }
1023
1024    // @VisibleForTesting
1025    public static int unpackTag(long packed) {
1026        return (int) (packed & 0xFFFFFFFFL);
1027    }
1028
1029    /**
1030     * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
1031     * in the given {@link NetworkIdentitySet}.
1032     */
1033    private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
1034        for (NetworkIdentity ident : identSet) {
1035            if (template.matches(ident)) {
1036                return true;
1037            }
1038        }
1039        return false;
1040    }
1041
1042    /**
1043     * Default external settings that read from {@link Settings.Secure}.
1044     */
1045    private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
1046        private final ContentResolver mResolver;
1047
1048        public DefaultNetworkStatsSettings(Context context) {
1049            mResolver = checkNotNull(context.getContentResolver());
1050            // TODO: adjust these timings for production builds
1051        }
1052
1053        private long getSecureLong(String name, long def) {
1054            return Settings.Secure.getLong(mResolver, name, def);
1055        }
1056
1057        public boolean getEnabled() {
1058            return Settings.Secure.getInt(mResolver, NETSTATS_ENABLED, 1) != 0;
1059        }
1060        public long getPollInterval() {
1061            return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
1062        }
1063        public long getPersistThreshold() {
1064            return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES);
1065        }
1066        public long getNetworkBucketDuration() {
1067            return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
1068        }
1069        public long getNetworkMaxHistory() {
1070            return getSecureLong(NETSTATS_NETWORK_MAX_HISTORY, 90 * DAY_IN_MILLIS);
1071        }
1072        public long getUidBucketDuration() {
1073            return getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
1074        }
1075        public long getUidMaxHistory() {
1076            return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS);
1077        }
1078        public long getTagMaxHistory() {
1079            return getSecureLong(NETSTATS_TAG_MAX_HISTORY, 30 * DAY_IN_MILLIS);
1080        }
1081        public long getTimeCacheMaxAge() {
1082            return DAY_IN_MILLIS;
1083        }
1084    }
1085
1086}
1087