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