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 17 package android.net; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.os.Build; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.os.Process; 28 import android.os.SystemClock; 29 import android.util.SparseBooleanArray; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.util.ArrayUtils; 33 34 import libcore.util.EmptyArray; 35 36 import java.io.CharArrayWriter; 37 import java.io.PrintWriter; 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.util.Arrays; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Objects; 45 import java.util.function.Predicate; 46 47 /** 48 * Collection of active network statistics. Can contain summary details across 49 * all interfaces, or details with per-UID granularity. Internally stores data 50 * as a large table, closely matching {@code /proc/} data format. This structure 51 * optimizes for rapid in-memory comparison, but consider using 52 * {@link NetworkStatsHistory} when persisting. 53 * 54 * @hide 55 */ 56 // @NotThreadSafe 57 @SystemApi 58 public final class NetworkStats implements Parcelable { 59 private static final String TAG = "NetworkStats"; 60 61 /** 62 * {@link #iface} value when interface details unavailable. 63 * @hide 64 */ 65 @Nullable public static final String IFACE_ALL = null; 66 67 /** 68 * Virtual network interface for video telephony. This is for VT data usage counting 69 * purpose. 70 */ 71 public static final String IFACE_VT = "vt_data0"; 72 73 /** {@link #uid} value when UID details unavailable. */ 74 public static final int UID_ALL = -1; 75 /** Special UID value for data usage by tethering. */ 76 public static final int UID_TETHERING = -5; 77 78 /** 79 * {@link #tag} value matching any tag. 80 * @hide 81 */ 82 // TODO: Rename TAG_ALL to TAG_ANY. 83 public static final int TAG_ALL = -1; 84 /** 85 * {@link #set} value for all sets combined, not including debug sets. 86 * @hide 87 */ 88 public static final int SET_ALL = -1; 89 /** {@link #set} value where background data is accounted. */ 90 public static final int SET_DEFAULT = 0; 91 /** {@link #set} value where foreground data is accounted. */ 92 public static final int SET_FOREGROUND = 1; 93 /** 94 * All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. 95 * @hide 96 */ 97 public static final int SET_DEBUG_START = 1000; 98 /** 99 * Debug {@link #set} value when the VPN stats are moved in. 100 * @hide 101 */ 102 public static final int SET_DBG_VPN_IN = 1001; 103 /** 104 * Debug {@link #set} value when the VPN stats are moved out of a vpn UID. 105 * @hide 106 */ 107 public static final int SET_DBG_VPN_OUT = 1002; 108 109 /** @hide */ 110 @Retention(RetentionPolicy.SOURCE) 111 @IntDef(prefix = { "SET_" }, value = { 112 SET_ALL, 113 SET_DEFAULT, 114 SET_FOREGROUND, 115 SET_DEBUG_START, 116 SET_DBG_VPN_IN, 117 SET_DBG_VPN_OUT 118 }) 119 public @interface State { 120 } 121 122 /** 123 * Include all interfaces when filtering 124 * @hide 125 */ 126 public @Nullable static final String[] INTERFACES_ALL = null; 127 128 /** {@link #tag} value for total data across all tags. */ 129 // TODO: Rename TAG_NONE to TAG_ALL. 130 public static final int TAG_NONE = 0; 131 132 /** 133 * {@link #metered} value to account for all metered states. 134 * @hide 135 */ 136 public static final int METERED_ALL = -1; 137 /** {@link #metered} value where native, unmetered data is accounted. */ 138 public static final int METERED_NO = 0; 139 /** {@link #metered} value where metered data is accounted. */ 140 public static final int METERED_YES = 1; 141 142 /** @hide */ 143 @Retention(RetentionPolicy.SOURCE) 144 @IntDef(prefix = { "METERED_" }, value = { 145 METERED_ALL, 146 METERED_NO, 147 METERED_YES 148 }) 149 public @interface Meteredness { 150 } 151 152 153 /** 154 * {@link #roaming} value to account for all roaming states. 155 * @hide 156 */ 157 public static final int ROAMING_ALL = -1; 158 /** {@link #roaming} value where native, non-roaming data is accounted. */ 159 public static final int ROAMING_NO = 0; 160 /** {@link #roaming} value where roaming data is accounted. */ 161 public static final int ROAMING_YES = 1; 162 163 /** @hide */ 164 @Retention(RetentionPolicy.SOURCE) 165 @IntDef(prefix = { "ROAMING_" }, value = { 166 ROAMING_ALL, 167 ROAMING_NO, 168 ROAMING_YES 169 }) 170 public @interface Roaming { 171 } 172 173 /** 174 * {@link #onDefaultNetwork} value to account for all default network states. 175 * @hide 176 */ 177 public static final int DEFAULT_NETWORK_ALL = -1; 178 /** {@link #onDefaultNetwork} value to account for usage while not the default network. */ 179 public static final int DEFAULT_NETWORK_NO = 0; 180 /** {@link #onDefaultNetwork} value to account for usage while the default network. */ 181 public static final int DEFAULT_NETWORK_YES = 1; 182 183 /** @hide */ 184 @Retention(RetentionPolicy.SOURCE) 185 @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = { 186 DEFAULT_NETWORK_ALL, 187 DEFAULT_NETWORK_NO, 188 DEFAULT_NETWORK_YES 189 }) 190 public @interface DefaultNetwork { 191 } 192 193 /** 194 * Denotes a request for stats at the interface level. 195 * @hide 196 */ 197 public static final int STATS_PER_IFACE = 0; 198 /** 199 * Denotes a request for stats at the interface and UID level. 200 * @hide 201 */ 202 public static final int STATS_PER_UID = 1; 203 204 /** @hide */ 205 @Retention(RetentionPolicy.SOURCE) 206 @IntDef(prefix = { "STATS_PER_" }, value = { 207 STATS_PER_IFACE, 208 STATS_PER_UID 209 }) 210 public @interface StatsType { 211 } 212 213 private static final String CLATD_INTERFACE_PREFIX = "v4-"; 214 // Delta between IPv4 header (20b) and IPv6 header (40b). 215 // Used for correct stats accounting on clatd interfaces. 216 private static final int IPV4V6_HEADER_DELTA = 20; 217 218 // TODO: move fields to "mVariable" notation 219 220 /** 221 * {@link SystemClock#elapsedRealtime()} timestamp when this data was 222 * generated. 223 */ 224 private long elapsedRealtime; 225 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 226 private int size; 227 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 228 private int capacity; 229 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 230 private String[] iface; 231 @UnsupportedAppUsage 232 private int[] uid; 233 @UnsupportedAppUsage 234 private int[] set; 235 @UnsupportedAppUsage 236 private int[] tag; 237 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 238 private int[] metered; 239 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 240 private int[] roaming; 241 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 242 private int[] defaultNetwork; 243 @UnsupportedAppUsage 244 private long[] rxBytes; 245 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 246 private long[] rxPackets; 247 @UnsupportedAppUsage 248 private long[] txBytes; 249 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 250 private long[] txPackets; 251 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 252 private long[] operations; 253 254 /** 255 * Basic element of network statistics. Contains the number of packets and number of bytes 256 * transferred on both directions in a given set of conditions. See 257 * {@link Entry#Entry(String, int, int, int, int, int, int, long, long, long, long, long)}. 258 * 259 * @hide 260 */ 261 @SystemApi 262 public static class Entry { 263 /** @hide */ 264 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 265 public String iface; 266 /** @hide */ 267 @UnsupportedAppUsage 268 public int uid; 269 /** @hide */ 270 @UnsupportedAppUsage 271 public int set; 272 /** @hide */ 273 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 274 public int tag; 275 /** 276 * Note that this is only populated w/ the default value when read from /proc or written 277 * to disk. We merge in the correct value when reporting this value to clients of 278 * getSummary(). 279 * @hide 280 */ 281 public int metered; 282 /** 283 * Note that this is only populated w/ the default value when read from /proc or written 284 * to disk. We merge in the correct value when reporting this value to clients of 285 * getSummary(). 286 * @hide 287 */ 288 public int roaming; 289 /** 290 * Note that this is only populated w/ the default value when read from /proc or written 291 * to disk. We merge in the correct value when reporting this value to clients of 292 * getSummary(). 293 * @hide 294 */ 295 public int defaultNetwork; 296 /** @hide */ 297 @UnsupportedAppUsage 298 public long rxBytes; 299 /** @hide */ 300 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 301 public long rxPackets; 302 /** @hide */ 303 @UnsupportedAppUsage 304 public long txBytes; 305 /** @hide */ 306 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 307 public long txPackets; 308 /** @hide */ 309 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 310 public long operations; 311 312 /** @hide */ 313 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) Entry()314 public Entry() { 315 this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L); 316 } 317 318 /** @hide */ Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)319 public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { 320 this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 321 operations); 322 } 323 324 /** @hide */ Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)325 public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, 326 long txBytes, long txPackets, long operations) { 327 this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 328 rxBytes, rxPackets, txBytes, txPackets, operations); 329 } 330 331 /** 332 * Construct a {@link Entry} object by giving statistics of packet and byte transferred on 333 * both direction, and associated with a set of given conditions. 334 * 335 * @param iface interface name of this {@link Entry}. Or null if not specified. 336 * @param uid uid of this {@link Entry}. {@link #UID_TETHERING} if this {@link Entry} is 337 * for tethering. Or {@link #UID_ALL} if this {@link NetworkStats} is only 338 * counting iface stats. 339 * @param set usage state of this {@link Entry}. Should be one of the following 340 * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}. 341 * @param tag tag of this {@link Entry}. 342 * @param metered metered state of this {@link Entry}. Should be one of the following 343 * values: {link #METERED_YES}, {link #METERED_NO}. 344 * @param roaming roaming state of this {@link Entry}. Should be one of the following 345 * values: {link #ROAMING_YES}, {link #ROAMING_NO}. 346 * @param defaultNetwork default network status of this {@link Entry}. Should be one 347 * of the following values: {link #DEFAULT_NETWORK_YES}, 348 * {link #DEFAULT_NETWORK_NO}. 349 * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should 350 * represent the contents of IP packets, including IP headers. 351 * @param rxPackets Number of packets received for this {@link Entry}. Statistics should 352 * represent the contents of IP packets, including IP headers. 353 * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should 354 * represent the contents of IP packets, including IP headers. 355 * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should 356 * represent the contents of IP packets, including IP headers. 357 * @param operations count of network operations performed for this {@link Entry}. This can 358 * be used to derive bytes-per-operation. 359 */ Entry(@ullable String iface, int uid, @State int set, int tag, @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)360 public Entry(@Nullable String iface, int uid, @State int set, int tag, 361 @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork, 362 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { 363 this.iface = iface; 364 this.uid = uid; 365 this.set = set; 366 this.tag = tag; 367 this.metered = metered; 368 this.roaming = roaming; 369 this.defaultNetwork = defaultNetwork; 370 this.rxBytes = rxBytes; 371 this.rxPackets = rxPackets; 372 this.txBytes = txBytes; 373 this.txPackets = txPackets; 374 this.operations = operations; 375 } 376 377 /** @hide */ isNegative()378 public boolean isNegative() { 379 return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0; 380 } 381 382 /** @hide */ isEmpty()383 public boolean isEmpty() { 384 return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0 385 && operations == 0; 386 } 387 388 /** @hide */ add(Entry another)389 public void add(Entry another) { 390 this.rxBytes += another.rxBytes; 391 this.rxPackets += another.rxPackets; 392 this.txBytes += another.txBytes; 393 this.txPackets += another.txPackets; 394 this.operations += another.operations; 395 } 396 397 @Override toString()398 public String toString() { 399 final StringBuilder builder = new StringBuilder(); 400 builder.append("iface=").append(iface); 401 builder.append(" uid=").append(uid); 402 builder.append(" set=").append(setToString(set)); 403 builder.append(" tag=").append(tagToString(tag)); 404 builder.append(" metered=").append(meteredToString(metered)); 405 builder.append(" roaming=").append(roamingToString(roaming)); 406 builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork)); 407 builder.append(" rxBytes=").append(rxBytes); 408 builder.append(" rxPackets=").append(rxPackets); 409 builder.append(" txBytes=").append(txBytes); 410 builder.append(" txPackets=").append(txPackets); 411 builder.append(" operations=").append(operations); 412 return builder.toString(); 413 } 414 415 /** @hide */ 416 @Override equals(@ullable Object o)417 public boolean equals(@Nullable Object o) { 418 if (o instanceof Entry) { 419 final Entry e = (Entry) o; 420 return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered 421 && roaming == e.roaming && defaultNetwork == e.defaultNetwork 422 && rxBytes == e.rxBytes && rxPackets == e.rxPackets 423 && txBytes == e.txBytes && txPackets == e.txPackets 424 && operations == e.operations && iface.equals(e.iface); 425 } 426 return false; 427 } 428 429 /** @hide */ 430 @Override hashCode()431 public int hashCode() { 432 return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface); 433 } 434 } 435 NetworkStats(long elapsedRealtime, int initialSize)436 public NetworkStats(long elapsedRealtime, int initialSize) { 437 this.elapsedRealtime = elapsedRealtime; 438 this.size = 0; 439 if (initialSize > 0) { 440 this.capacity = initialSize; 441 this.iface = new String[initialSize]; 442 this.uid = new int[initialSize]; 443 this.set = new int[initialSize]; 444 this.tag = new int[initialSize]; 445 this.metered = new int[initialSize]; 446 this.roaming = new int[initialSize]; 447 this.defaultNetwork = new int[initialSize]; 448 this.rxBytes = new long[initialSize]; 449 this.rxPackets = new long[initialSize]; 450 this.txBytes = new long[initialSize]; 451 this.txPackets = new long[initialSize]; 452 this.operations = new long[initialSize]; 453 } else { 454 // Special case for use by NetworkStatsFactory to start out *really* empty. 455 clear(); 456 } 457 } 458 459 /** @hide */ 460 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) NetworkStats(Parcel parcel)461 public NetworkStats(Parcel parcel) { 462 elapsedRealtime = parcel.readLong(); 463 size = parcel.readInt(); 464 capacity = parcel.readInt(); 465 iface = parcel.createStringArray(); 466 uid = parcel.createIntArray(); 467 set = parcel.createIntArray(); 468 tag = parcel.createIntArray(); 469 metered = parcel.createIntArray(); 470 roaming = parcel.createIntArray(); 471 defaultNetwork = parcel.createIntArray(); 472 rxBytes = parcel.createLongArray(); 473 rxPackets = parcel.createLongArray(); 474 txBytes = parcel.createLongArray(); 475 txPackets = parcel.createLongArray(); 476 operations = parcel.createLongArray(); 477 } 478 479 @Override writeToParcel(@onNull Parcel dest, int flags)480 public void writeToParcel(@NonNull Parcel dest, int flags) { 481 dest.writeLong(elapsedRealtime); 482 dest.writeInt(size); 483 dest.writeInt(capacity); 484 dest.writeStringArray(iface); 485 dest.writeIntArray(uid); 486 dest.writeIntArray(set); 487 dest.writeIntArray(tag); 488 dest.writeIntArray(metered); 489 dest.writeIntArray(roaming); 490 dest.writeIntArray(defaultNetwork); 491 dest.writeLongArray(rxBytes); 492 dest.writeLongArray(rxPackets); 493 dest.writeLongArray(txBytes); 494 dest.writeLongArray(txPackets); 495 dest.writeLongArray(operations); 496 } 497 498 /** 499 * @hide 500 */ 501 @Override clone()502 public NetworkStats clone() { 503 final NetworkStats clone = new NetworkStats(elapsedRealtime, size); 504 NetworkStats.Entry entry = null; 505 for (int i = 0; i < size; i++) { 506 entry = getValues(i, entry); 507 clone.insertEntry(entry); 508 } 509 return clone; 510 } 511 512 /** 513 * Clear all data stored in this object. 514 * @hide 515 */ clear()516 public void clear() { 517 this.capacity = 0; 518 this.iface = EmptyArray.STRING; 519 this.uid = EmptyArray.INT; 520 this.set = EmptyArray.INT; 521 this.tag = EmptyArray.INT; 522 this.metered = EmptyArray.INT; 523 this.roaming = EmptyArray.INT; 524 this.defaultNetwork = EmptyArray.INT; 525 this.rxBytes = EmptyArray.LONG; 526 this.rxPackets = EmptyArray.LONG; 527 this.txBytes = EmptyArray.LONG; 528 this.txPackets = EmptyArray.LONG; 529 this.operations = EmptyArray.LONG; 530 } 531 532 /** @hide */ 533 @VisibleForTesting insertEntry( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets)534 public NetworkStats insertEntry( 535 String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { 536 return insertEntry( 537 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L); 538 } 539 540 /** @hide */ 541 @VisibleForTesting insertEntry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)542 public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes, 543 long rxPackets, long txBytes, long txPackets, long operations) { 544 return insertEntry(new Entry( 545 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); 546 } 547 548 /** @hide */ 549 @VisibleForTesting insertEntry(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)550 public NetworkStats insertEntry(String iface, int uid, int set, int tag, int metered, 551 int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, 552 long txPackets, long operations) { 553 return insertEntry(new Entry( 554 iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets, 555 txBytes, txPackets, operations)); 556 } 557 558 /** 559 * Add new stats entry, copying from given {@link Entry}. The {@link Entry} 560 * object can be recycled across multiple calls. 561 * @hide 562 */ insertEntry(Entry entry)563 public NetworkStats insertEntry(Entry entry) { 564 if (size >= capacity) { 565 final int newLength = Math.max(size, 10) * 3 / 2; 566 iface = Arrays.copyOf(iface, newLength); 567 uid = Arrays.copyOf(uid, newLength); 568 set = Arrays.copyOf(set, newLength); 569 tag = Arrays.copyOf(tag, newLength); 570 metered = Arrays.copyOf(metered, newLength); 571 roaming = Arrays.copyOf(roaming, newLength); 572 defaultNetwork = Arrays.copyOf(defaultNetwork, newLength); 573 rxBytes = Arrays.copyOf(rxBytes, newLength); 574 rxPackets = Arrays.copyOf(rxPackets, newLength); 575 txBytes = Arrays.copyOf(txBytes, newLength); 576 txPackets = Arrays.copyOf(txPackets, newLength); 577 operations = Arrays.copyOf(operations, newLength); 578 capacity = newLength; 579 } 580 581 setValues(size, entry); 582 size++; 583 584 return this; 585 } 586 setValues(int i, Entry entry)587 private void setValues(int i, Entry entry) { 588 iface[i] = entry.iface; 589 uid[i] = entry.uid; 590 set[i] = entry.set; 591 tag[i] = entry.tag; 592 metered[i] = entry.metered; 593 roaming[i] = entry.roaming; 594 defaultNetwork[i] = entry.defaultNetwork; 595 rxBytes[i] = entry.rxBytes; 596 rxPackets[i] = entry.rxPackets; 597 txBytes[i] = entry.txBytes; 598 txPackets[i] = entry.txPackets; 599 operations[i] = entry.operations; 600 } 601 602 /** 603 * Return specific stats entry. 604 * @hide 605 */ 606 @UnsupportedAppUsage getValues(int i, Entry recycle)607 public Entry getValues(int i, Entry recycle) { 608 final Entry entry = recycle != null ? recycle : new Entry(); 609 entry.iface = iface[i]; 610 entry.uid = uid[i]; 611 entry.set = set[i]; 612 entry.tag = tag[i]; 613 entry.metered = metered[i]; 614 entry.roaming = roaming[i]; 615 entry.defaultNetwork = defaultNetwork[i]; 616 entry.rxBytes = rxBytes[i]; 617 entry.rxPackets = rxPackets[i]; 618 entry.txBytes = txBytes[i]; 619 entry.txPackets = txPackets[i]; 620 entry.operations = operations[i]; 621 return entry; 622 } 623 624 /** 625 * If @{code dest} is not equal to @{code src}, copy entry from index @{code src} to index 626 * @{code dest}. 627 */ maybeCopyEntry(int dest, int src)628 private void maybeCopyEntry(int dest, int src) { 629 if (dest == src) return; 630 iface[dest] = iface[src]; 631 uid[dest] = uid[src]; 632 set[dest] = set[src]; 633 tag[dest] = tag[src]; 634 metered[dest] = metered[src]; 635 roaming[dest] = roaming[src]; 636 defaultNetwork[dest] = defaultNetwork[src]; 637 rxBytes[dest] = rxBytes[src]; 638 rxPackets[dest] = rxPackets[src]; 639 txBytes[dest] = txBytes[src]; 640 txPackets[dest] = txPackets[src]; 641 operations[dest] = operations[src]; 642 } 643 644 /** @hide */ getElapsedRealtime()645 public long getElapsedRealtime() { 646 return elapsedRealtime; 647 } 648 649 /** @hide */ setElapsedRealtime(long time)650 public void setElapsedRealtime(long time) { 651 elapsedRealtime = time; 652 } 653 654 /** 655 * Return age of this {@link NetworkStats} object with respect to 656 * {@link SystemClock#elapsedRealtime()}. 657 * @hide 658 */ getElapsedRealtimeAge()659 public long getElapsedRealtimeAge() { 660 return SystemClock.elapsedRealtime() - elapsedRealtime; 661 } 662 663 /** @hide */ 664 @UnsupportedAppUsage size()665 public int size() { 666 return size; 667 } 668 669 /** @hide */ 670 @VisibleForTesting internalSize()671 public int internalSize() { 672 return capacity; 673 } 674 675 /** @hide */ 676 @Deprecated combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)677 public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, 678 long txBytes, long txPackets, long operations) { 679 return combineValues( 680 iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes, 681 txPackets, operations); 682 } 683 684 /** @hide */ combineValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)685 public NetworkStats combineValues(String iface, int uid, int set, int tag, 686 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { 687 return combineValues(new Entry( 688 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); 689 } 690 691 /** 692 * Combine given values with an existing row, or create a new row if 693 * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can 694 * also be used to subtract values from existing rows. This method mutates the referencing 695 * {@link NetworkStats} object. 696 * 697 * @param entry the {@link Entry} to combine. 698 * @return a reference to this mutated {@link NetworkStats} object. 699 * @hide 700 */ combineValues(@onNull Entry entry)701 public @NonNull NetworkStats combineValues(@NonNull Entry entry) { 702 final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered, 703 entry.roaming, entry.defaultNetwork); 704 if (i == -1) { 705 // only create new entry when positive contribution 706 insertEntry(entry); 707 } else { 708 rxBytes[i] += entry.rxBytes; 709 rxPackets[i] += entry.rxPackets; 710 txBytes[i] += entry.txBytes; 711 txPackets[i] += entry.txPackets; 712 operations[i] += entry.operations; 713 } 714 return this; 715 } 716 717 /** 718 * Add given values with an existing row, or create a new row if 719 * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can 720 * also be used to subtract values from existing rows. 721 * 722 * @param entry the {@link Entry} to add. 723 * @return a new constructed {@link NetworkStats} object that contains the result. 724 */ addEntry(@onNull Entry entry)725 public @NonNull NetworkStats addEntry(@NonNull Entry entry) { 726 return this.clone().combineValues(entry); 727 } 728 729 /** 730 * Add the given {@link NetworkStats} objects. 731 * 732 * @return the sum of two objects. 733 */ add(@onNull NetworkStats another)734 public @NonNull NetworkStats add(@NonNull NetworkStats another) { 735 final NetworkStats ret = this.clone(); 736 ret.combineAllValues(another); 737 return ret; 738 } 739 740 /** 741 * Combine all values from another {@link NetworkStats} into this object. 742 * @hide 743 */ combineAllValues(@onNull NetworkStats another)744 public void combineAllValues(@NonNull NetworkStats another) { 745 NetworkStats.Entry entry = null; 746 for (int i = 0; i < another.size; i++) { 747 entry = another.getValues(i, entry); 748 combineValues(entry); 749 } 750 } 751 752 /** 753 * Find first stats index that matches the requested parameters. 754 * @hide 755 */ findIndex(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork)756 public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming, 757 int defaultNetwork) { 758 for (int i = 0; i < size; i++) { 759 if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i] 760 && metered == this.metered[i] && roaming == this.roaming[i] 761 && defaultNetwork == this.defaultNetwork[i] 762 && Objects.equals(iface, this.iface[i])) { 763 return i; 764 } 765 } 766 return -1; 767 } 768 769 /** 770 * Find first stats index that matches the requested parameters, starting 771 * search around the hinted index as an optimization. 772 * @hide 773 */ 774 @VisibleForTesting findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, int hintIndex)775 public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, 776 int defaultNetwork, int hintIndex) { 777 for (int offset = 0; offset < size; offset++) { 778 final int halfOffset = offset / 2; 779 780 // search outwards from hint index, alternating forward and backward 781 final int i; 782 if (offset % 2 == 0) { 783 i = (hintIndex + halfOffset) % size; 784 } else { 785 i = (size + hintIndex - halfOffset - 1) % size; 786 } 787 788 if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i] 789 && metered == this.metered[i] && roaming == this.roaming[i] 790 && defaultNetwork == this.defaultNetwork[i] 791 && Objects.equals(iface, this.iface[i])) { 792 return i; 793 } 794 } 795 return -1; 796 } 797 798 /** 799 * Splice in {@link #operations} from the given {@link NetworkStats} based 800 * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface}, 801 * since operation counts are at data layer. 802 * @hide 803 */ spliceOperationsFrom(NetworkStats stats)804 public void spliceOperationsFrom(NetworkStats stats) { 805 for (int i = 0; i < size; i++) { 806 final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i], 807 defaultNetwork[i]); 808 if (j == -1) { 809 operations[i] = 0; 810 } else { 811 operations[i] = stats.operations[j]; 812 } 813 } 814 } 815 816 /** 817 * Return list of unique interfaces known by this data structure. 818 * @hide 819 */ getUniqueIfaces()820 public String[] getUniqueIfaces() { 821 final HashSet<String> ifaces = new HashSet<String>(); 822 for (String iface : this.iface) { 823 if (iface != IFACE_ALL) { 824 ifaces.add(iface); 825 } 826 } 827 return ifaces.toArray(new String[ifaces.size()]); 828 } 829 830 /** 831 * Return list of unique UIDs known by this data structure. 832 * @hide 833 */ 834 @UnsupportedAppUsage getUniqueUids()835 public int[] getUniqueUids() { 836 final SparseBooleanArray uids = new SparseBooleanArray(); 837 for (int uid : this.uid) { 838 uids.put(uid, true); 839 } 840 841 final int size = uids.size(); 842 final int[] result = new int[size]; 843 for (int i = 0; i < size; i++) { 844 result[i] = uids.keyAt(i); 845 } 846 return result; 847 } 848 849 /** 850 * Return total bytes represented by this snapshot object, usually used when 851 * checking if a {@link #subtract(NetworkStats)} delta passes a threshold. 852 * @hide 853 */ 854 @UnsupportedAppUsage getTotalBytes()855 public long getTotalBytes() { 856 final Entry entry = getTotal(null); 857 return entry.rxBytes + entry.txBytes; 858 } 859 860 /** 861 * Return total of all fields represented by this snapshot object. 862 * @hide 863 */ 864 @UnsupportedAppUsage getTotal(Entry recycle)865 public Entry getTotal(Entry recycle) { 866 return getTotal(recycle, null, UID_ALL, false); 867 } 868 869 /** 870 * Return total of all fields represented by this snapshot object matching 871 * the requested {@link #uid}. 872 * @hide 873 */ 874 @UnsupportedAppUsage getTotal(Entry recycle, int limitUid)875 public Entry getTotal(Entry recycle, int limitUid) { 876 return getTotal(recycle, null, limitUid, false); 877 } 878 879 /** 880 * Return total of all fields represented by this snapshot object matching 881 * the requested {@link #iface}. 882 * @hide 883 */ getTotal(Entry recycle, HashSet<String> limitIface)884 public Entry getTotal(Entry recycle, HashSet<String> limitIface) { 885 return getTotal(recycle, limitIface, UID_ALL, false); 886 } 887 888 /** @hide */ 889 @UnsupportedAppUsage getTotalIncludingTags(Entry recycle)890 public Entry getTotalIncludingTags(Entry recycle) { 891 return getTotal(recycle, null, UID_ALL, true); 892 } 893 894 /** 895 * Return total of all fields represented by this snapshot object matching 896 * the requested {@link #iface} and {@link #uid}. 897 * 898 * @param limitIface Set of {@link #iface} to include in total; or {@code 899 * null} to include all ifaces. 900 */ getTotal( Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags)901 private Entry getTotal( 902 Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) { 903 final Entry entry = recycle != null ? recycle : new Entry(); 904 905 entry.iface = IFACE_ALL; 906 entry.uid = limitUid; 907 entry.set = SET_ALL; 908 entry.tag = TAG_NONE; 909 entry.metered = METERED_ALL; 910 entry.roaming = ROAMING_ALL; 911 entry.defaultNetwork = DEFAULT_NETWORK_ALL; 912 entry.rxBytes = 0; 913 entry.rxPackets = 0; 914 entry.txBytes = 0; 915 entry.txPackets = 0; 916 entry.operations = 0; 917 918 for (int i = 0; i < size; i++) { 919 final boolean matchesUid = (limitUid == UID_ALL) || (limitUid == uid[i]); 920 final boolean matchesIface = (limitIface == null) || (limitIface.contains(iface[i])); 921 922 if (matchesUid && matchesIface) { 923 // skip specific tags, since already counted in TAG_NONE 924 if (tag[i] != TAG_NONE && !includeTags) continue; 925 926 entry.rxBytes += rxBytes[i]; 927 entry.rxPackets += rxPackets[i]; 928 entry.txBytes += txBytes[i]; 929 entry.txPackets += txPackets[i]; 930 entry.operations += operations[i]; 931 } 932 } 933 return entry; 934 } 935 936 /** 937 * Fast path for battery stats. 938 * @hide 939 */ getTotalPackets()940 public long getTotalPackets() { 941 long total = 0; 942 for (int i = size-1; i >= 0; i--) { 943 total += rxPackets[i] + txPackets[i]; 944 } 945 return total; 946 } 947 948 /** 949 * Subtract the given {@link NetworkStats}, effectively leaving the delta 950 * between two snapshots in time. Assumes that statistics rows collect over 951 * time, and that none of them have disappeared. This method does not mutate 952 * the referencing object. 953 * 954 * @return the delta between two objects. 955 */ subtract(@onNull NetworkStats right)956 public @NonNull NetworkStats subtract(@NonNull NetworkStats right) { 957 return subtract(this, right, null, null); 958 } 959 960 /** 961 * Subtract the two given {@link NetworkStats} objects, returning the delta 962 * between two snapshots in time. Assumes that statistics rows collect over 963 * time, and that none of them have disappeared. 964 * <p> 965 * If counters have rolled backwards, they are clamped to {@code 0} and 966 * reported to the given {@link NonMonotonicObserver}. 967 * @hide 968 */ subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie)969 public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right, 970 NonMonotonicObserver<C> observer, C cookie) { 971 return subtract(left, right, observer, cookie, null); 972 } 973 974 /** 975 * Subtract the two given {@link NetworkStats} objects, returning the delta 976 * between two snapshots in time. Assumes that statistics rows collect over 977 * time, and that none of them have disappeared. 978 * <p> 979 * If counters have rolled backwards, they are clamped to {@code 0} and 980 * reported to the given {@link NonMonotonicObserver}. 981 * <p> 982 * If <var>recycle</var> is supplied, this NetworkStats object will be 983 * reused (and returned) as the result if it is large enough to contain 984 * the data. 985 * @hide 986 */ subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle)987 public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right, 988 NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) { 989 long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime; 990 if (deltaRealtime < 0) { 991 if (observer != null) { 992 observer.foundNonMonotonic(left, -1, right, -1, cookie); 993 } 994 deltaRealtime = 0; 995 } 996 997 // result will have our rows, and elapsed time between snapshots 998 final Entry entry = new Entry(); 999 final NetworkStats result; 1000 if (recycle != null && recycle.capacity >= left.size) { 1001 result = recycle; 1002 result.size = 0; 1003 result.elapsedRealtime = deltaRealtime; 1004 } else { 1005 result = new NetworkStats(deltaRealtime, left.size); 1006 } 1007 for (int i = 0; i < left.size; i++) { 1008 entry.iface = left.iface[i]; 1009 entry.uid = left.uid[i]; 1010 entry.set = left.set[i]; 1011 entry.tag = left.tag[i]; 1012 entry.metered = left.metered[i]; 1013 entry.roaming = left.roaming[i]; 1014 entry.defaultNetwork = left.defaultNetwork[i]; 1015 entry.rxBytes = left.rxBytes[i]; 1016 entry.rxPackets = left.rxPackets[i]; 1017 entry.txBytes = left.txBytes[i]; 1018 entry.txPackets = left.txPackets[i]; 1019 entry.operations = left.operations[i]; 1020 1021 // find remote row that matches, and subtract 1022 final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, 1023 entry.metered, entry.roaming, entry.defaultNetwork, i); 1024 if (j != -1) { 1025 // Found matching row, subtract remote value. 1026 entry.rxBytes -= right.rxBytes[j]; 1027 entry.rxPackets -= right.rxPackets[j]; 1028 entry.txBytes -= right.txBytes[j]; 1029 entry.txPackets -= right.txPackets[j]; 1030 entry.operations -= right.operations[j]; 1031 } 1032 1033 if (entry.isNegative()) { 1034 if (observer != null) { 1035 observer.foundNonMonotonic(left, i, right, j, cookie); 1036 } 1037 entry.rxBytes = Math.max(entry.rxBytes, 0); 1038 entry.rxPackets = Math.max(entry.rxPackets, 0); 1039 entry.txBytes = Math.max(entry.txBytes, 0); 1040 entry.txPackets = Math.max(entry.txPackets, 0); 1041 entry.operations = Math.max(entry.operations, 0); 1042 } 1043 1044 result.insertEntry(entry); 1045 } 1046 1047 return result; 1048 } 1049 1050 /** 1051 * Calculate and apply adjustments to captured statistics for 464xlat traffic. 1052 * 1053 * <p>This mutates stacked traffic stats, to account for IPv4/IPv6 header size difference. 1054 * 1055 * <p>UID stats, which are only accounted on the stacked interface, need to be increased 1056 * by 20 bytes/packet to account for translation overhead. 1057 * 1058 * <p>The potential additional overhead of 8 bytes/packet for ip fragments is ignored. 1059 * 1060 * <p>Interface stats need to sum traffic on both stacked and base interface because: 1061 * - eBPF offloaded packets appear only on the stacked interface 1062 * - Non-offloaded ingress packets appear only on the stacked interface 1063 * (due to iptables raw PREROUTING drop rules) 1064 * - Non-offloaded egress packets appear only on the stacked interface 1065 * (due to ignoring traffic from clat daemon by uid match) 1066 * (and of course the 20 bytes/packet overhead needs to be applied to stacked interface stats) 1067 * 1068 * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only 1069 * {@code ConcurrentHashMap} 1070 * @param baseTraffic Traffic on the base interfaces. Will be mutated. 1071 * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated. 1072 * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both. 1073 * @hide 1074 */ apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic, Map<String, String> stackedIfaces)1075 public static void apply464xlatAdjustments(NetworkStats baseTraffic, 1076 NetworkStats stackedTraffic, Map<String, String> stackedIfaces) { 1077 // For recycling 1078 Entry entry = null; 1079 for (int i = 0; i < stackedTraffic.size; i++) { 1080 entry = stackedTraffic.getValues(i, entry); 1081 if (entry == null) continue; 1082 if (entry.iface == null) continue; 1083 if (!entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) continue; 1084 1085 // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet 1086 // sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after 1087 // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes 1088 // difference for all packets (http://b/12249687, http:/b/33681750). 1089 // 1090 // Note: this doesn't account for LRO/GRO/GSO/TSO (ie. >mtu) traffic correctly, nor 1091 // does it correctly account for the 8 extra bytes in the IPv6 fragmentation header. 1092 // 1093 // While the ebpf code path does try to simulate proper post segmentation packet 1094 // counts, we have nothing of the sort of xt_qtaguid stats. 1095 entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA; 1096 entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA; 1097 stackedTraffic.setValues(i, entry); 1098 } 1099 } 1100 1101 /** 1102 * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice. 1103 * 1104 * <p>This mutates the object this method is called on. Equivalent to calling 1105 * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as 1106 * base and stacked traffic. 1107 * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both. 1108 * @hide 1109 */ apply464xlatAdjustments(Map<String, String> stackedIfaces)1110 public void apply464xlatAdjustments(Map<String, String> stackedIfaces) { 1111 apply464xlatAdjustments(this, this, stackedIfaces); 1112 } 1113 1114 /** 1115 * Return total statistics grouped by {@link #iface}; doesn't mutate the 1116 * original structure. 1117 * @hide 1118 */ groupedByIface()1119 public NetworkStats groupedByIface() { 1120 final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); 1121 1122 final Entry entry = new Entry(); 1123 entry.uid = UID_ALL; 1124 entry.set = SET_ALL; 1125 entry.tag = TAG_NONE; 1126 entry.metered = METERED_ALL; 1127 entry.roaming = ROAMING_ALL; 1128 entry.defaultNetwork = DEFAULT_NETWORK_ALL; 1129 entry.operations = 0L; 1130 1131 for (int i = 0; i < size; i++) { 1132 // skip specific tags, since already counted in TAG_NONE 1133 if (tag[i] != TAG_NONE) continue; 1134 1135 entry.iface = iface[i]; 1136 entry.rxBytes = rxBytes[i]; 1137 entry.rxPackets = rxPackets[i]; 1138 entry.txBytes = txBytes[i]; 1139 entry.txPackets = txPackets[i]; 1140 stats.combineValues(entry); 1141 } 1142 1143 return stats; 1144 } 1145 1146 /** 1147 * Return total statistics grouped by {@link #uid}; doesn't mutate the 1148 * original structure. 1149 * @hide 1150 */ groupedByUid()1151 public NetworkStats groupedByUid() { 1152 final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); 1153 1154 final Entry entry = new Entry(); 1155 entry.iface = IFACE_ALL; 1156 entry.set = SET_ALL; 1157 entry.tag = TAG_NONE; 1158 entry.metered = METERED_ALL; 1159 entry.roaming = ROAMING_ALL; 1160 entry.defaultNetwork = DEFAULT_NETWORK_ALL; 1161 1162 for (int i = 0; i < size; i++) { 1163 // skip specific tags, since already counted in TAG_NONE 1164 if (tag[i] != TAG_NONE) continue; 1165 1166 entry.uid = uid[i]; 1167 entry.rxBytes = rxBytes[i]; 1168 entry.rxPackets = rxPackets[i]; 1169 entry.txBytes = txBytes[i]; 1170 entry.txPackets = txPackets[i]; 1171 entry.operations = operations[i]; 1172 stats.combineValues(entry); 1173 } 1174 1175 return stats; 1176 } 1177 1178 /** 1179 * Remove all rows that match one of specified UIDs. 1180 * This mutates the original structure in place. 1181 * @hide 1182 */ removeUids(int[] uids)1183 public void removeUids(int[] uids) { 1184 filter(e -> !ArrayUtils.contains(uids, e.uid)); 1185 } 1186 1187 /** 1188 * Remove all rows that match one of specified UIDs. 1189 * @return the result object. 1190 * @hide 1191 */ 1192 @NonNull removeEmptyEntries()1193 public NetworkStats removeEmptyEntries() { 1194 final NetworkStats ret = this.clone(); 1195 ret.filter(e -> e.rxBytes != 0 || e.rxPackets != 0 || e.txBytes != 0 || e.txPackets != 0 1196 || e.operations != 0); 1197 return ret; 1198 } 1199 1200 /** 1201 * Only keep entries that match all specified filters. 1202 * 1203 * <p>This mutates the original structure in place. After this method is called, 1204 * size is the number of matching entries, and capacity is the previous capacity. 1205 * @param limitUid UID to filter for, or {@link #UID_ALL}. 1206 * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}. 1207 * @param limitTag Tag to filter for, or {@link #TAG_ALL}. 1208 * @hide 1209 */ filter(int limitUid, String[] limitIfaces, int limitTag)1210 public void filter(int limitUid, String[] limitIfaces, int limitTag) { 1211 if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) { 1212 return; 1213 } 1214 filter(e -> (limitUid == UID_ALL || limitUid == e.uid) 1215 && (limitTag == TAG_ALL || limitTag == e.tag) 1216 && (limitIfaces == INTERFACES_ALL 1217 || ArrayUtils.contains(limitIfaces, e.iface))); 1218 } 1219 1220 /** 1221 * Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}. 1222 * 1223 * <p>This mutates the original structure in place. 1224 * @hide 1225 */ filterDebugEntries()1226 public void filterDebugEntries() { 1227 filter(e -> e.set < SET_DEBUG_START); 1228 } 1229 filter(Predicate<Entry> predicate)1230 private void filter(Predicate<Entry> predicate) { 1231 Entry entry = new Entry(); 1232 int nextOutputEntry = 0; 1233 for (int i = 0; i < size; i++) { 1234 entry = getValues(i, entry); 1235 if (predicate.test(entry)) { 1236 if (nextOutputEntry != i) { 1237 setValues(nextOutputEntry, entry); 1238 } 1239 nextOutputEntry++; 1240 } 1241 } 1242 size = nextOutputEntry; 1243 } 1244 1245 /** @hide */ dump(String prefix, PrintWriter pw)1246 public void dump(String prefix, PrintWriter pw) { 1247 pw.print(prefix); 1248 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); 1249 for (int i = 0; i < size; i++) { 1250 pw.print(prefix); 1251 pw.print(" ["); pw.print(i); pw.print("]"); 1252 pw.print(" iface="); pw.print(iface[i]); 1253 pw.print(" uid="); pw.print(uid[i]); 1254 pw.print(" set="); pw.print(setToString(set[i])); 1255 pw.print(" tag="); pw.print(tagToString(tag[i])); 1256 pw.print(" metered="); pw.print(meteredToString(metered[i])); 1257 pw.print(" roaming="); pw.print(roamingToString(roaming[i])); 1258 pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i])); 1259 pw.print(" rxBytes="); pw.print(rxBytes[i]); 1260 pw.print(" rxPackets="); pw.print(rxPackets[i]); 1261 pw.print(" txBytes="); pw.print(txBytes[i]); 1262 pw.print(" txPackets="); pw.print(txPackets[i]); 1263 pw.print(" operations="); pw.println(operations[i]); 1264 } 1265 } 1266 1267 /** 1268 * Return text description of {@link #set} value. 1269 * @hide 1270 */ setToString(int set)1271 public static String setToString(int set) { 1272 switch (set) { 1273 case SET_ALL: 1274 return "ALL"; 1275 case SET_DEFAULT: 1276 return "DEFAULT"; 1277 case SET_FOREGROUND: 1278 return "FOREGROUND"; 1279 case SET_DBG_VPN_IN: 1280 return "DBG_VPN_IN"; 1281 case SET_DBG_VPN_OUT: 1282 return "DBG_VPN_OUT"; 1283 default: 1284 return "UNKNOWN"; 1285 } 1286 } 1287 1288 /** 1289 * Return text description of {@link #set} value. 1290 * @hide 1291 */ setToCheckinString(int set)1292 public static String setToCheckinString(int set) { 1293 switch (set) { 1294 case SET_ALL: 1295 return "all"; 1296 case SET_DEFAULT: 1297 return "def"; 1298 case SET_FOREGROUND: 1299 return "fg"; 1300 case SET_DBG_VPN_IN: 1301 return "vpnin"; 1302 case SET_DBG_VPN_OUT: 1303 return "vpnout"; 1304 default: 1305 return "unk"; 1306 } 1307 } 1308 1309 /** 1310 * @return true if the querySet matches the dataSet. 1311 * @hide 1312 */ setMatches(int querySet, int dataSet)1313 public static boolean setMatches(int querySet, int dataSet) { 1314 if (querySet == dataSet) { 1315 return true; 1316 } 1317 // SET_ALL matches all non-debugging sets. 1318 return querySet == SET_ALL && dataSet < SET_DEBUG_START; 1319 } 1320 1321 /** 1322 * Return text description of {@link #tag} value. 1323 * @hide 1324 */ tagToString(int tag)1325 public static String tagToString(int tag) { 1326 return "0x" + Integer.toHexString(tag); 1327 } 1328 1329 /** 1330 * Return text description of {@link #metered} value. 1331 * @hide 1332 */ meteredToString(int metered)1333 public static String meteredToString(int metered) { 1334 switch (metered) { 1335 case METERED_ALL: 1336 return "ALL"; 1337 case METERED_NO: 1338 return "NO"; 1339 case METERED_YES: 1340 return "YES"; 1341 default: 1342 return "UNKNOWN"; 1343 } 1344 } 1345 1346 /** 1347 * Return text description of {@link #roaming} value. 1348 * @hide 1349 */ roamingToString(int roaming)1350 public static String roamingToString(int roaming) { 1351 switch (roaming) { 1352 case ROAMING_ALL: 1353 return "ALL"; 1354 case ROAMING_NO: 1355 return "NO"; 1356 case ROAMING_YES: 1357 return "YES"; 1358 default: 1359 return "UNKNOWN"; 1360 } 1361 } 1362 1363 /** 1364 * Return text description of {@link #defaultNetwork} value. 1365 * @hide 1366 */ defaultNetworkToString(int defaultNetwork)1367 public static String defaultNetworkToString(int defaultNetwork) { 1368 switch (defaultNetwork) { 1369 case DEFAULT_NETWORK_ALL: 1370 return "ALL"; 1371 case DEFAULT_NETWORK_NO: 1372 return "NO"; 1373 case DEFAULT_NETWORK_YES: 1374 return "YES"; 1375 default: 1376 return "UNKNOWN"; 1377 } 1378 } 1379 1380 /** @hide */ 1381 @Override toString()1382 public String toString() { 1383 final CharArrayWriter writer = new CharArrayWriter(); 1384 dump("", new PrintWriter(writer)); 1385 return writer.toString(); 1386 } 1387 1388 @Override describeContents()1389 public int describeContents() { 1390 return 0; 1391 } 1392 1393 public static final @NonNull Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { 1394 @Override 1395 public NetworkStats createFromParcel(Parcel in) { 1396 return new NetworkStats(in); 1397 } 1398 1399 @Override 1400 public NetworkStats[] newArray(int size) { 1401 return new NetworkStats[size]; 1402 } 1403 }; 1404 1405 /** @hide */ 1406 public interface NonMonotonicObserver<C> { foundNonMonotonic( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie)1407 public void foundNonMonotonic( 1408 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie); foundNonMonotonic( NetworkStats stats, int statsIndex, C cookie)1409 public void foundNonMonotonic( 1410 NetworkStats stats, int statsIndex, C cookie); 1411 } 1412 1413 /** 1414 * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface. 1415 * 1416 * <p>This method should only be called on delta NetworkStats. Do not call this method on a 1417 * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may change 1418 * over time. 1419 * 1420 * <p>This method performs adjustments for one active VPN package and one VPN iface at a time. 1421 * 1422 * @param tunUid uid of the VPN application 1423 * @param tunIface iface of the vpn tunnel 1424 * @param underlyingIfaces underlying network ifaces used by the VPN application 1425 * @hide 1426 */ migrateTun(int tunUid, @NonNull String tunIface, @NonNull List<String> underlyingIfaces)1427 public void migrateTun(int tunUid, @NonNull String tunIface, 1428 @NonNull List<String> underlyingIfaces) { 1429 // Combined usage by all apps using VPN. 1430 final Entry tunIfaceTotal = new Entry(); 1431 // Usage by VPN, grouped by its {@code underlyingIfaces}. 1432 final Entry[] perInterfaceTotal = new Entry[underlyingIfaces.size()]; 1433 // Usage by VPN, summed across all its {@code underlyingIfaces}. 1434 final Entry underlyingIfacesTotal = new Entry(); 1435 1436 for (int i = 0; i < perInterfaceTotal.length; i++) { 1437 perInterfaceTotal[i] = new Entry(); 1438 } 1439 1440 tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal, 1441 underlyingIfacesTotal); 1442 1443 // If tunIface < underlyingIfacesTotal, it leaves the overhead traffic in the VPN app. 1444 // If tunIface > underlyingIfacesTotal, the VPN app doesn't get credit for data compression. 1445 // Negative stats should be avoided. 1446 final Entry[] moved = 1447 addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, 1448 perInterfaceTotal, underlyingIfacesTotal); 1449 deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved); 1450 } 1451 1452 /** 1453 * Initializes the data used by the migrateTun() method. 1454 * 1455 * <p>This is the first pass iteration which does the following work: 1456 * 1457 * <ul> 1458 * <li>Adds up all the traffic through the tunUid's underlyingIfaces (both foreground and 1459 * background). 1460 * <li>Adds up all the traffic through tun0 excluding traffic from the vpn app itself. 1461 * </ul> 1462 * 1463 * @param tunUid uid of the VPN application 1464 * @param tunIface iface of the vpn tunnel 1465 * @param underlyingIfaces underlying network ifaces used by the VPN application 1466 * @param tunIfaceTotal output parameter; combined data usage by all apps using VPN 1467 * @param perInterfaceTotal output parameter; data usage by VPN app, grouped by its {@code 1468 * underlyingIfaces} 1469 * @param underlyingIfacesTotal output parameter; data usage by VPN, summed across all of its 1470 * {@code underlyingIfaces} 1471 */ tunAdjustmentInit(int tunUid, @NonNull String tunIface, @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal, @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal)1472 private void tunAdjustmentInit(int tunUid, @NonNull String tunIface, 1473 @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal, 1474 @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) { 1475 final Entry recycle = new Entry(); 1476 for (int i = 0; i < size; i++) { 1477 getValues(i, recycle); 1478 if (recycle.uid == UID_ALL) { 1479 throw new IllegalStateException( 1480 "Cannot adjust VPN accounting on an iface aggregated NetworkStats."); 1481 } 1482 if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) { 1483 throw new IllegalStateException( 1484 "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*"); 1485 } 1486 if (recycle.tag != TAG_NONE) { 1487 // TODO(b/123666283): Take all tags for tunUid into account. 1488 continue; 1489 } 1490 1491 if (tunUid == Process.SYSTEM_UID) { 1492 // Kernel-based VPN or VCN, traffic sent by apps on the VPN/VCN network 1493 // 1494 // Since the data is not UID-accounted on underlying networks, just use VPN/VCN 1495 // network usage as ground truth. Encrypted traffic on the underlying networks will 1496 // never be processed here because encrypted traffic on the underlying interfaces 1497 // is not present in UID stats, and this method is only called on UID stats. 1498 if (tunIface.equals(recycle.iface)) { 1499 tunIfaceTotal.add(recycle); 1500 underlyingIfacesTotal.add(recycle); 1501 1502 // In steady state, there should always be one network, but edge cases may 1503 // result in the network being null (network lost), and thus no underlying 1504 // ifaces is possible. 1505 if (perInterfaceTotal.length > 0) { 1506 // While platform VPNs and VCNs have exactly one underlying network, that 1507 // network may have multiple interfaces (eg for 464xlat). This layer does 1508 // not have the required information to identify which of the interfaces 1509 // were used. Select "any" of the interfaces. Since overhead is already 1510 // lost, this number is an approximation anyways. 1511 perInterfaceTotal[0].add(recycle); 1512 } 1513 } 1514 } else if (recycle.uid == tunUid) { 1515 // VpnService VPN, traffic sent by the VPN app over underlying networks 1516 for (int j = 0; j < underlyingIfaces.size(); j++) { 1517 if (Objects.equals(underlyingIfaces.get(j), recycle.iface)) { 1518 perInterfaceTotal[j].add(recycle); 1519 underlyingIfacesTotal.add(recycle); 1520 break; 1521 } 1522 } 1523 } else if (tunIface.equals(recycle.iface)) { 1524 // VpnService VPN; traffic sent by apps on the VPN network 1525 tunIfaceTotal.add(recycle); 1526 } 1527 } 1528 } 1529 1530 /** 1531 * Distributes traffic across apps that are using given {@code tunIface}, and returns the total 1532 * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}. 1533 * 1534 * @param tunUid uid of the VPN application 1535 * @param tunIface iface of the vpn tunnel 1536 * @param underlyingIfaces underlying network ifaces used by the VPN application 1537 * @param tunIfaceTotal combined data usage across all apps using {@code tunIface} 1538 * @param perInterfaceTotal data usage by VPN app, grouped by its {@code underlyingIfaces} 1539 * @param underlyingIfacesTotal data usage by VPN, summed across all of its {@code 1540 * underlyingIfaces} 1541 */ addTrafficToApplications(int tunUid, @NonNull String tunIface, @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal, @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal)1542 private Entry[] addTrafficToApplications(int tunUid, @NonNull String tunIface, 1543 @NonNull List<String> underlyingIfaces, @NonNull Entry tunIfaceTotal, 1544 @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) { 1545 // Traffic that should be moved off of each underlying interface for tunUid (see 1546 // deductTrafficFromVpnApp below). 1547 final Entry[] moved = new Entry[underlyingIfaces.size()]; 1548 for (int i = 0; i < underlyingIfaces.size(); i++) { 1549 moved[i] = new Entry(); 1550 } 1551 1552 final Entry tmpEntry = new Entry(); 1553 final int origSize = size; 1554 for (int i = 0; i < origSize; i++) { 1555 if (!Objects.equals(iface[i], tunIface)) { 1556 // Consider only entries that go onto the VPN interface. 1557 continue; 1558 } 1559 1560 if (uid[i] == tunUid && tunUid != Process.SYSTEM_UID) { 1561 // Exclude VPN app from the redistribution, as it can choose to create packet 1562 // streams by writing to itself. 1563 // 1564 // However, for platform VPNs, do not exclude the system's usage of the VPN network, 1565 // since it is never local-only, and never double counted 1566 continue; 1567 } 1568 tmpEntry.uid = uid[i]; 1569 tmpEntry.tag = tag[i]; 1570 tmpEntry.metered = metered[i]; 1571 tmpEntry.roaming = roaming[i]; 1572 tmpEntry.defaultNetwork = defaultNetwork[i]; 1573 1574 // In a first pass, compute this entry's total share of data across all 1575 // underlyingIfaces. This is computed on the basis of the share of this entry's usage 1576 // over tunIface. 1577 // TODO: Consider refactoring first pass into a separate helper method. 1578 long totalRxBytes = 0; 1579 if (tunIfaceTotal.rxBytes > 0) { 1580 // Note - The multiplication below should not overflow since NetworkStatsService 1581 // processes this every time device has transmitted/received amount equivalent to 1582 // global threshold alert (~ 2MB) across all interfaces. 1583 final long rxBytesAcrossUnderlyingIfaces = 1584 underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; 1585 // app must not be blamed for more than it consumed on tunIface 1586 totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces); 1587 } 1588 long totalRxPackets = 0; 1589 if (tunIfaceTotal.rxPackets > 0) { 1590 final long rxPacketsAcrossUnderlyingIfaces = 1591 underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; 1592 totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces); 1593 } 1594 long totalTxBytes = 0; 1595 if (tunIfaceTotal.txBytes > 0) { 1596 final long txBytesAcrossUnderlyingIfaces = 1597 underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes; 1598 totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces); 1599 } 1600 long totalTxPackets = 0; 1601 if (tunIfaceTotal.txPackets > 0) { 1602 final long txPacketsAcrossUnderlyingIfaces = 1603 underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets; 1604 totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces); 1605 } 1606 long totalOperations = 0; 1607 if (tunIfaceTotal.operations > 0) { 1608 final long operationsAcrossUnderlyingIfaces = 1609 underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations; 1610 totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces); 1611 } 1612 // In a second pass, distribute these values across interfaces in the proportion that 1613 // each interface represents of the total traffic of the underlying interfaces. 1614 for (int j = 0; j < underlyingIfaces.size(); j++) { 1615 tmpEntry.iface = underlyingIfaces.get(j); 1616 tmpEntry.rxBytes = 0; 1617 // Reset 'set' to correct value since it gets updated when adding debug info below. 1618 tmpEntry.set = set[i]; 1619 if (underlyingIfacesTotal.rxBytes > 0) { 1620 tmpEntry.rxBytes = 1621 totalRxBytes 1622 * perInterfaceTotal[j].rxBytes 1623 / underlyingIfacesTotal.rxBytes; 1624 } 1625 tmpEntry.rxPackets = 0; 1626 if (underlyingIfacesTotal.rxPackets > 0) { 1627 tmpEntry.rxPackets = 1628 totalRxPackets 1629 * perInterfaceTotal[j].rxPackets 1630 / underlyingIfacesTotal.rxPackets; 1631 } 1632 tmpEntry.txBytes = 0; 1633 if (underlyingIfacesTotal.txBytes > 0) { 1634 tmpEntry.txBytes = 1635 totalTxBytes 1636 * perInterfaceTotal[j].txBytes 1637 / underlyingIfacesTotal.txBytes; 1638 } 1639 tmpEntry.txPackets = 0; 1640 if (underlyingIfacesTotal.txPackets > 0) { 1641 tmpEntry.txPackets = 1642 totalTxPackets 1643 * perInterfaceTotal[j].txPackets 1644 / underlyingIfacesTotal.txPackets; 1645 } 1646 tmpEntry.operations = 0; 1647 if (underlyingIfacesTotal.operations > 0) { 1648 tmpEntry.operations = 1649 totalOperations 1650 * perInterfaceTotal[j].operations 1651 / underlyingIfacesTotal.operations; 1652 } 1653 // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying 1654 // interface. Add that data usage to this object. 1655 combineValues(tmpEntry); 1656 if (tag[i] == TAG_NONE) { 1657 // Add the migrated data to moved so it is deducted from the VPN app later. 1658 moved[j].add(tmpEntry); 1659 // Add debug info 1660 tmpEntry.set = SET_DBG_VPN_IN; 1661 combineValues(tmpEntry); 1662 } 1663 } 1664 } 1665 return moved; 1666 } 1667 deductTrafficFromVpnApp( int tunUid, @NonNull List<String> underlyingIfaces, @NonNull Entry[] moved)1668 private void deductTrafficFromVpnApp( 1669 int tunUid, 1670 @NonNull List<String> underlyingIfaces, 1671 @NonNull Entry[] moved) { 1672 if (tunUid == Process.SYSTEM_UID) { 1673 // No traffic recorded on a per-UID basis for in-kernel VPN/VCNs over underlying 1674 // networks; thus no traffic to deduct. 1675 return; 1676 } 1677 1678 for (int i = 0; i < underlyingIfaces.size(); i++) { 1679 moved[i].uid = tunUid; 1680 // Add debug info 1681 moved[i].set = SET_DBG_VPN_OUT; 1682 moved[i].tag = TAG_NONE; 1683 moved[i].iface = underlyingIfaces.get(i); 1684 moved[i].metered = METERED_ALL; 1685 moved[i].roaming = ROAMING_ALL; 1686 moved[i].defaultNetwork = DEFAULT_NETWORK_ALL; 1687 combineValues(moved[i]); 1688 1689 // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than 1690 // the TAG_NONE traffic. 1691 // 1692 // Relies on the fact that the underlying traffic only has state ROAMING_NO and 1693 // METERED_NO, which should be the case as it comes directly from the /proc file. 1694 // We only blend in the roaming data after applying these adjustments, by checking the 1695 // NetworkIdentity of the underlying iface. 1696 final int idxVpnBackground = findIndex(underlyingIfaces.get(i), tunUid, SET_DEFAULT, 1697 TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); 1698 if (idxVpnBackground != -1) { 1699 // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed 1700 // from foreground usage. 1701 tunSubtract(idxVpnBackground, this, moved[i]); 1702 } 1703 1704 final int idxVpnForeground = findIndex(underlyingIfaces.get(i), tunUid, SET_FOREGROUND, 1705 TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); 1706 if (idxVpnForeground != -1) { 1707 tunSubtract(idxVpnForeground, this, moved[i]); 1708 } 1709 } 1710 } 1711 tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right)1712 private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) { 1713 long rxBytes = Math.min(left.rxBytes[i], right.rxBytes); 1714 left.rxBytes[i] -= rxBytes; 1715 right.rxBytes -= rxBytes; 1716 1717 long rxPackets = Math.min(left.rxPackets[i], right.rxPackets); 1718 left.rxPackets[i] -= rxPackets; 1719 right.rxPackets -= rxPackets; 1720 1721 long txBytes = Math.min(left.txBytes[i], right.txBytes); 1722 left.txBytes[i] -= txBytes; 1723 right.txBytes -= txBytes; 1724 1725 long txPackets = Math.min(left.txPackets[i], right.txPackets); 1726 left.txPackets[i] -= txPackets; 1727 right.txPackets -= txPackets; 1728 } 1729 } 1730