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