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