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