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