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