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