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