1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.os.SystemClock; 22 import android.util.Slog; 23 import android.util.SparseBooleanArray; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 import com.android.internal.util.ArrayUtils; 27 28 import libcore.util.EmptyArray; 29 30 import java.io.CharArrayWriter; 31 import java.io.PrintWriter; 32 import java.util.Arrays; 33 import java.util.HashSet; 34 import java.util.Map; 35 import java.util.Objects; 36 37 /** 38 * Collection of active network statistics. Can contain summary details across 39 * all interfaces, or details with per-UID granularity. Internally stores data 40 * as a large table, closely matching {@code /proc/} data format. This structure 41 * optimizes for rapid in-memory comparison, but consider using 42 * {@link NetworkStatsHistory} when persisting. 43 * 44 * @hide 45 */ 46 public class NetworkStats implements Parcelable { 47 private static final String TAG = "NetworkStats"; 48 /** {@link #iface} value when interface details unavailable. */ 49 public static final String IFACE_ALL = null; 50 /** {@link #uid} value when UID details unavailable. */ 51 public static final int UID_ALL = -1; 52 /** {@link #tag} value matching any tag. */ 53 // TODO: Rename TAG_ALL to TAG_ANY. 54 public static final int TAG_ALL = -1; 55 /** {@link #set} value for all sets combined, not including debug sets. */ 56 public static final int SET_ALL = -1; 57 /** {@link #set} value where background data is accounted. */ 58 public static final int SET_DEFAULT = 0; 59 /** {@link #set} value where foreground data is accounted. */ 60 public static final int SET_FOREGROUND = 1; 61 /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */ 62 public static final int SET_DEBUG_START = 1000; 63 /** Debug {@link #set} value when the VPN stats are moved in. */ 64 public static final int SET_DBG_VPN_IN = 1001; 65 /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */ 66 public static final int SET_DBG_VPN_OUT = 1002; 67 68 /** Include all interfaces when filtering */ 69 public static final String[] INTERFACES_ALL = null; 70 71 /** {@link #tag} value for total data across all tags. */ 72 // TODO: Rename TAG_NONE to TAG_ALL. 73 public static final int TAG_NONE = 0; 74 75 /** {@link #metered} value to account for all metered states. */ 76 public static final int METERED_ALL = -1; 77 /** {@link #metered} value where native, unmetered data is accounted. */ 78 public static final int METERED_NO = 0; 79 /** {@link #metered} value where metered data is accounted. */ 80 public static final int METERED_YES = 1; 81 82 /** {@link #roaming} value to account for all roaming states. */ 83 public static final int ROAMING_ALL = -1; 84 /** {@link #roaming} value where native, non-roaming data is accounted. */ 85 public static final int ROAMING_NO = 0; 86 /** {@link #roaming} value where roaming data is accounted. */ 87 public static final int ROAMING_YES = 1; 88 89 /** {@link #onDefaultNetwork} value to account for all default network states. */ 90 public static final int DEFAULT_NETWORK_ALL = -1; 91 /** {@link #onDefaultNetwork} value to account for usage while not the default network. */ 92 public static final int DEFAULT_NETWORK_NO = 0; 93 /** {@link #onDefaultNetwork} value to account for usage while the default network. */ 94 public static final int DEFAULT_NETWORK_YES = 1; 95 96 /** Denotes a request for stats at the interface level. */ 97 public static final int STATS_PER_IFACE = 0; 98 /** Denotes a request for stats at the interface and UID level. */ 99 public static final int STATS_PER_UID = 1; 100 101 private static final String CLATD_INTERFACE_PREFIX = "v4-"; 102 // Delta between IPv4 header (20b) and IPv6 header (40b). 103 // Used for correct stats accounting on clatd interfaces. 104 private static final int IPV4V6_HEADER_DELTA = 20; 105 106 // TODO: move fields to "mVariable" notation 107 108 /** 109 * {@link SystemClock#elapsedRealtime()} timestamp when this data was 110 * generated. 111 */ 112 private long elapsedRealtime; 113 private int size; 114 private int capacity; 115 private String[] iface; 116 private int[] uid; 117 private int[] set; 118 private int[] tag; 119 private int[] metered; 120 private int[] roaming; 121 private int[] defaultNetwork; 122 private long[] rxBytes; 123 private long[] rxPackets; 124 private long[] txBytes; 125 private long[] txPackets; 126 private long[] operations; 127 128 public static class Entry { 129 public String iface; 130 public int uid; 131 public int set; 132 public int tag; 133 /** 134 * Note that this is only populated w/ the default value when read from /proc or written 135 * to disk. We merge in the correct value when reporting this value to clients of 136 * getSummary(). 137 */ 138 public int metered; 139 /** 140 * Note that this is only populated w/ the default value when read from /proc or written 141 * to disk. We merge in the correct value when reporting this value to clients of 142 * getSummary(). 143 */ 144 public int roaming; 145 /** 146 * Note that this is only populated w/ the default value when read from /proc or written 147 * to disk. We merge in the correct value when reporting this value to clients of 148 * getSummary(). 149 */ 150 public int defaultNetwork; 151 public long rxBytes; 152 public long rxPackets; 153 public long txBytes; 154 public long txPackets; 155 public long operations; 156 Entry()157 public Entry() { 158 this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L); 159 } 160 Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)161 public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { 162 this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 163 operations); 164 } 165 Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)166 public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, 167 long txBytes, long txPackets, long operations) { 168 this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 169 rxBytes, rxPackets, txBytes, txPackets, operations); 170 } 171 Entry(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)172 public Entry(String iface, int uid, int set, int tag, int metered, int roaming, 173 int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, 174 long operations) { 175 this.iface = iface; 176 this.uid = uid; 177 this.set = set; 178 this.tag = tag; 179 this.metered = metered; 180 this.roaming = roaming; 181 this.defaultNetwork = defaultNetwork; 182 this.rxBytes = rxBytes; 183 this.rxPackets = rxPackets; 184 this.txBytes = txBytes; 185 this.txPackets = txPackets; 186 this.operations = operations; 187 } 188 isNegative()189 public boolean isNegative() { 190 return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0; 191 } 192 isEmpty()193 public boolean isEmpty() { 194 return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0 195 && operations == 0; 196 } 197 add(Entry another)198 public void add(Entry another) { 199 this.rxBytes += another.rxBytes; 200 this.rxPackets += another.rxPackets; 201 this.txBytes += another.txBytes; 202 this.txPackets += another.txPackets; 203 this.operations += another.operations; 204 } 205 206 @Override toString()207 public String toString() { 208 final StringBuilder builder = new StringBuilder(); 209 builder.append("iface=").append(iface); 210 builder.append(" uid=").append(uid); 211 builder.append(" set=").append(setToString(set)); 212 builder.append(" tag=").append(tagToString(tag)); 213 builder.append(" metered=").append(meteredToString(metered)); 214 builder.append(" roaming=").append(roamingToString(roaming)); 215 builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork)); 216 builder.append(" rxBytes=").append(rxBytes); 217 builder.append(" rxPackets=").append(rxPackets); 218 builder.append(" txBytes=").append(txBytes); 219 builder.append(" txPackets=").append(txPackets); 220 builder.append(" operations=").append(operations); 221 return builder.toString(); 222 } 223 224 @Override equals(Object o)225 public boolean equals(Object o) { 226 if (o instanceof Entry) { 227 final Entry e = (Entry) o; 228 return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered 229 && roaming == e.roaming && defaultNetwork == e.defaultNetwork 230 && rxBytes == e.rxBytes && rxPackets == e.rxPackets 231 && txBytes == e.txBytes && txPackets == e.txPackets 232 && operations == e.operations && iface.equals(e.iface); 233 } 234 return false; 235 } 236 237 @Override hashCode()238 public int hashCode() { 239 return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface); 240 } 241 } 242 NetworkStats(long elapsedRealtime, int initialSize)243 public NetworkStats(long elapsedRealtime, int initialSize) { 244 this.elapsedRealtime = elapsedRealtime; 245 this.size = 0; 246 if (initialSize > 0) { 247 this.capacity = initialSize; 248 this.iface = new String[initialSize]; 249 this.uid = new int[initialSize]; 250 this.set = new int[initialSize]; 251 this.tag = new int[initialSize]; 252 this.metered = new int[initialSize]; 253 this.roaming = new int[initialSize]; 254 this.defaultNetwork = new int[initialSize]; 255 this.rxBytes = new long[initialSize]; 256 this.rxPackets = new long[initialSize]; 257 this.txBytes = new long[initialSize]; 258 this.txPackets = new long[initialSize]; 259 this.operations = new long[initialSize]; 260 } else { 261 // Special case for use by NetworkStatsFactory to start out *really* empty. 262 clear(); 263 } 264 } 265 NetworkStats(Parcel parcel)266 public NetworkStats(Parcel parcel) { 267 elapsedRealtime = parcel.readLong(); 268 size = parcel.readInt(); 269 capacity = parcel.readInt(); 270 iface = parcel.createStringArray(); 271 uid = parcel.createIntArray(); 272 set = parcel.createIntArray(); 273 tag = parcel.createIntArray(); 274 metered = parcel.createIntArray(); 275 roaming = parcel.createIntArray(); 276 defaultNetwork = parcel.createIntArray(); 277 rxBytes = parcel.createLongArray(); 278 rxPackets = parcel.createLongArray(); 279 txBytes = parcel.createLongArray(); 280 txPackets = parcel.createLongArray(); 281 operations = parcel.createLongArray(); 282 } 283 284 @Override writeToParcel(Parcel dest, int flags)285 public void writeToParcel(Parcel dest, int flags) { 286 dest.writeLong(elapsedRealtime); 287 dest.writeInt(size); 288 dest.writeInt(capacity); 289 dest.writeStringArray(iface); 290 dest.writeIntArray(uid); 291 dest.writeIntArray(set); 292 dest.writeIntArray(tag); 293 dest.writeIntArray(metered); 294 dest.writeIntArray(roaming); 295 dest.writeIntArray(defaultNetwork); 296 dest.writeLongArray(rxBytes); 297 dest.writeLongArray(rxPackets); 298 dest.writeLongArray(txBytes); 299 dest.writeLongArray(txPackets); 300 dest.writeLongArray(operations); 301 } 302 303 @Override clone()304 public NetworkStats clone() { 305 final NetworkStats clone = new NetworkStats(elapsedRealtime, size); 306 NetworkStats.Entry entry = null; 307 for (int i = 0; i < size; i++) { 308 entry = getValues(i, entry); 309 clone.addValues(entry); 310 } 311 return clone; 312 } 313 314 /** 315 * Clear all data stored in this object. 316 */ clear()317 public void clear() { 318 this.capacity = 0; 319 this.iface = EmptyArray.STRING; 320 this.uid = EmptyArray.INT; 321 this.set = EmptyArray.INT; 322 this.tag = EmptyArray.INT; 323 this.metered = EmptyArray.INT; 324 this.roaming = EmptyArray.INT; 325 this.defaultNetwork = EmptyArray.INT; 326 this.rxBytes = EmptyArray.LONG; 327 this.rxPackets = EmptyArray.LONG; 328 this.txBytes = EmptyArray.LONG; 329 this.txPackets = EmptyArray.LONG; 330 this.operations = EmptyArray.LONG; 331 } 332 333 @VisibleForTesting addIfaceValues( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets)334 public NetworkStats addIfaceValues( 335 String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { 336 return addValues( 337 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L); 338 } 339 340 @VisibleForTesting addValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)341 public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes, 342 long rxPackets, long txBytes, long txPackets, long operations) { 343 return addValues(new Entry( 344 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); 345 } 346 347 @VisibleForTesting addValues(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)348 public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming, 349 int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, 350 long operations) { 351 return addValues(new Entry( 352 iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets, 353 txBytes, txPackets, operations)); 354 } 355 356 /** 357 * Add new stats entry, copying from given {@link Entry}. The {@link Entry} 358 * object can be recycled across multiple calls. 359 */ addValues(Entry entry)360 public NetworkStats addValues(Entry entry) { 361 if (size >= capacity) { 362 final int newLength = Math.max(size, 10) * 3 / 2; 363 iface = Arrays.copyOf(iface, newLength); 364 uid = Arrays.copyOf(uid, newLength); 365 set = Arrays.copyOf(set, newLength); 366 tag = Arrays.copyOf(tag, newLength); 367 metered = Arrays.copyOf(metered, newLength); 368 roaming = Arrays.copyOf(roaming, newLength); 369 defaultNetwork = Arrays.copyOf(defaultNetwork, newLength); 370 rxBytes = Arrays.copyOf(rxBytes, newLength); 371 rxPackets = Arrays.copyOf(rxPackets, newLength); 372 txBytes = Arrays.copyOf(txBytes, newLength); 373 txPackets = Arrays.copyOf(txPackets, newLength); 374 operations = Arrays.copyOf(operations, newLength); 375 capacity = newLength; 376 } 377 378 setValues(size, entry); 379 size++; 380 381 return this; 382 } 383 setValues(int i, Entry entry)384 private void setValues(int i, Entry entry) { 385 iface[i] = entry.iface; 386 uid[i] = entry.uid; 387 set[i] = entry.set; 388 tag[i] = entry.tag; 389 metered[i] = entry.metered; 390 roaming[i] = entry.roaming; 391 defaultNetwork[i] = entry.defaultNetwork; 392 rxBytes[i] = entry.rxBytes; 393 rxPackets[i] = entry.rxPackets; 394 txBytes[i] = entry.txBytes; 395 txPackets[i] = entry.txPackets; 396 operations[i] = entry.operations; 397 } 398 399 /** 400 * Return specific stats entry. 401 */ getValues(int i, Entry recycle)402 public Entry getValues(int i, Entry recycle) { 403 final Entry entry = recycle != null ? recycle : new Entry(); 404 entry.iface = iface[i]; 405 entry.uid = uid[i]; 406 entry.set = set[i]; 407 entry.tag = tag[i]; 408 entry.metered = metered[i]; 409 entry.roaming = roaming[i]; 410 entry.defaultNetwork = defaultNetwork[i]; 411 entry.rxBytes = rxBytes[i]; 412 entry.rxPackets = rxPackets[i]; 413 entry.txBytes = txBytes[i]; 414 entry.txPackets = txPackets[i]; 415 entry.operations = operations[i]; 416 return entry; 417 } 418 getElapsedRealtime()419 public long getElapsedRealtime() { 420 return elapsedRealtime; 421 } 422 setElapsedRealtime(long time)423 public void setElapsedRealtime(long time) { 424 elapsedRealtime = time; 425 } 426 427 /** 428 * Return age of this {@link NetworkStats} object with respect to 429 * {@link SystemClock#elapsedRealtime()}. 430 */ getElapsedRealtimeAge()431 public long getElapsedRealtimeAge() { 432 return SystemClock.elapsedRealtime() - elapsedRealtime; 433 } 434 size()435 public int size() { 436 return size; 437 } 438 439 @VisibleForTesting internalSize()440 public int internalSize() { 441 return capacity; 442 } 443 444 @Deprecated combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)445 public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, 446 long txBytes, long txPackets, long operations) { 447 return combineValues( 448 iface, uid, SET_DEFAULT, tag, rxBytes, rxPackets, txBytes, 449 txPackets, operations); 450 } 451 combineValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations)452 public NetworkStats combineValues(String iface, int uid, int set, int tag, 453 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { 454 return combineValues(new Entry( 455 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); 456 } 457 458 /** 459 * Combine given values with an existing row, or create a new row if 460 * {@link #findIndex(String, int, int, int, int)} is unable to find match. Can 461 * also be used to subtract values from existing rows. 462 */ combineValues(Entry entry)463 public NetworkStats combineValues(Entry entry) { 464 final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered, 465 entry.roaming, entry.defaultNetwork); 466 if (i == -1) { 467 // only create new entry when positive contribution 468 addValues(entry); 469 } else { 470 rxBytes[i] += entry.rxBytes; 471 rxPackets[i] += entry.rxPackets; 472 txBytes[i] += entry.txBytes; 473 txPackets[i] += entry.txPackets; 474 operations[i] += entry.operations; 475 } 476 return this; 477 } 478 479 /** 480 * Combine all values from another {@link NetworkStats} into this object. 481 */ combineAllValues(NetworkStats another)482 public void combineAllValues(NetworkStats another) { 483 NetworkStats.Entry entry = null; 484 for (int i = 0; i < another.size; i++) { 485 entry = another.getValues(i, entry); 486 combineValues(entry); 487 } 488 } 489 490 /** 491 * Find first stats index that matches the requested parameters. 492 */ findIndex(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork)493 public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming, 494 int defaultNetwork) { 495 for (int i = 0; i < size; i++) { 496 if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i] 497 && metered == this.metered[i] && roaming == this.roaming[i] 498 && defaultNetwork == this.defaultNetwork[i] 499 && Objects.equals(iface, this.iface[i])) { 500 return i; 501 } 502 } 503 return -1; 504 } 505 506 /** 507 * Find first stats index that matches the requested parameters, starting 508 * search around the hinted index as an optimization. 509 */ 510 @VisibleForTesting findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, int hintIndex)511 public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, 512 int defaultNetwork, int hintIndex) { 513 for (int offset = 0; offset < size; offset++) { 514 final int halfOffset = offset / 2; 515 516 // search outwards from hint index, alternating forward and backward 517 final int i; 518 if (offset % 2 == 0) { 519 i = (hintIndex + halfOffset) % size; 520 } else { 521 i = (size + hintIndex - halfOffset - 1) % size; 522 } 523 524 if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i] 525 && metered == this.metered[i] && roaming == this.roaming[i] 526 && defaultNetwork == this.defaultNetwork[i] 527 && Objects.equals(iface, this.iface[i])) { 528 return i; 529 } 530 } 531 return -1; 532 } 533 534 /** 535 * Splice in {@link #operations} from the given {@link NetworkStats} based 536 * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface}, 537 * since operation counts are at data layer. 538 */ spliceOperationsFrom(NetworkStats stats)539 public void spliceOperationsFrom(NetworkStats stats) { 540 for (int i = 0; i < size; i++) { 541 final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i], 542 defaultNetwork[i]); 543 if (j == -1) { 544 operations[i] = 0; 545 } else { 546 operations[i] = stats.operations[j]; 547 } 548 } 549 } 550 551 /** 552 * Return list of unique interfaces known by this data structure. 553 */ getUniqueIfaces()554 public String[] getUniqueIfaces() { 555 final HashSet<String> ifaces = new HashSet<String>(); 556 for (String iface : this.iface) { 557 if (iface != IFACE_ALL) { 558 ifaces.add(iface); 559 } 560 } 561 return ifaces.toArray(new String[ifaces.size()]); 562 } 563 564 /** 565 * Return list of unique UIDs known by this data structure. 566 */ getUniqueUids()567 public int[] getUniqueUids() { 568 final SparseBooleanArray uids = new SparseBooleanArray(); 569 for (int uid : this.uid) { 570 uids.put(uid, true); 571 } 572 573 final int size = uids.size(); 574 final int[] result = new int[size]; 575 for (int i = 0; i < size; i++) { 576 result[i] = uids.keyAt(i); 577 } 578 return result; 579 } 580 581 /** 582 * Return total bytes represented by this snapshot object, usually used when 583 * checking if a {@link #subtract(NetworkStats)} delta passes a threshold. 584 */ getTotalBytes()585 public long getTotalBytes() { 586 final Entry entry = getTotal(null); 587 return entry.rxBytes + entry.txBytes; 588 } 589 590 /** 591 * Return total of all fields represented by this snapshot object. 592 */ getTotal(Entry recycle)593 public Entry getTotal(Entry recycle) { 594 return getTotal(recycle, null, UID_ALL, false); 595 } 596 597 /** 598 * Return total of all fields represented by this snapshot object matching 599 * the requested {@link #uid}. 600 */ getTotal(Entry recycle, int limitUid)601 public Entry getTotal(Entry recycle, int limitUid) { 602 return getTotal(recycle, null, limitUid, false); 603 } 604 605 /** 606 * Return total of all fields represented by this snapshot object matching 607 * the requested {@link #iface}. 608 */ getTotal(Entry recycle, HashSet<String> limitIface)609 public Entry getTotal(Entry recycle, HashSet<String> limitIface) { 610 return getTotal(recycle, limitIface, UID_ALL, false); 611 } 612 getTotalIncludingTags(Entry recycle)613 public Entry getTotalIncludingTags(Entry recycle) { 614 return getTotal(recycle, null, UID_ALL, true); 615 } 616 617 /** 618 * Return total of all fields represented by this snapshot object matching 619 * the requested {@link #iface} and {@link #uid}. 620 * 621 * @param limitIface Set of {@link #iface} to include in total; or {@code 622 * null} to include all ifaces. 623 */ getTotal( Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags)624 private Entry getTotal( 625 Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) { 626 final Entry entry = recycle != null ? recycle : new Entry(); 627 628 entry.iface = IFACE_ALL; 629 entry.uid = limitUid; 630 entry.set = SET_ALL; 631 entry.tag = TAG_NONE; 632 entry.metered = METERED_ALL; 633 entry.roaming = ROAMING_ALL; 634 entry.defaultNetwork = DEFAULT_NETWORK_ALL; 635 entry.rxBytes = 0; 636 entry.rxPackets = 0; 637 entry.txBytes = 0; 638 entry.txPackets = 0; 639 entry.operations = 0; 640 641 for (int i = 0; i < size; i++) { 642 final boolean matchesUid = (limitUid == UID_ALL) || (limitUid == uid[i]); 643 final boolean matchesIface = (limitIface == null) || (limitIface.contains(iface[i])); 644 645 if (matchesUid && matchesIface) { 646 // skip specific tags, since already counted in TAG_NONE 647 if (tag[i] != TAG_NONE && !includeTags) continue; 648 649 entry.rxBytes += rxBytes[i]; 650 entry.rxPackets += rxPackets[i]; 651 entry.txBytes += txBytes[i]; 652 entry.txPackets += txPackets[i]; 653 entry.operations += operations[i]; 654 } 655 } 656 return entry; 657 } 658 659 /** 660 * Fast path for battery stats. 661 */ getTotalPackets()662 public long getTotalPackets() { 663 long total = 0; 664 for (int i = size-1; i >= 0; i--) { 665 total += rxPackets[i] + txPackets[i]; 666 } 667 return total; 668 } 669 670 /** 671 * Subtract the given {@link NetworkStats}, effectively leaving the delta 672 * between two snapshots in time. Assumes that statistics rows collect over 673 * time, and that none of them have disappeared. 674 */ subtract(NetworkStats right)675 public NetworkStats subtract(NetworkStats right) { 676 return subtract(this, right, null, null); 677 } 678 679 /** 680 * Subtract the two given {@link NetworkStats} objects, returning the delta 681 * between two snapshots in time. Assumes that statistics rows collect over 682 * time, and that none of them have disappeared. 683 * <p> 684 * If counters have rolled backwards, they are clamped to {@code 0} and 685 * reported to the given {@link NonMonotonicObserver}. 686 */ subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie)687 public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right, 688 NonMonotonicObserver<C> observer, C cookie) { 689 return subtract(left, right, observer, cookie, null); 690 } 691 692 /** 693 * Subtract the two given {@link NetworkStats} objects, returning the delta 694 * between two snapshots in time. Assumes that statistics rows collect over 695 * time, and that none of them have disappeared. 696 * <p> 697 * If counters have rolled backwards, they are clamped to {@code 0} and 698 * reported to the given {@link NonMonotonicObserver}. 699 * <p> 700 * If <var>recycle</var> is supplied, this NetworkStats object will be 701 * reused (and returned) as the result if it is large enough to contain 702 * the data. 703 */ subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle)704 public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right, 705 NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) { 706 long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime; 707 if (deltaRealtime < 0) { 708 if (observer != null) { 709 observer.foundNonMonotonic(left, -1, right, -1, cookie); 710 } 711 deltaRealtime = 0; 712 } 713 714 // result will have our rows, and elapsed time between snapshots 715 final Entry entry = new Entry(); 716 final NetworkStats result; 717 if (recycle != null && recycle.capacity >= left.size) { 718 result = recycle; 719 result.size = 0; 720 result.elapsedRealtime = deltaRealtime; 721 } else { 722 result = new NetworkStats(deltaRealtime, left.size); 723 } 724 for (int i = 0; i < left.size; i++) { 725 entry.iface = left.iface[i]; 726 entry.uid = left.uid[i]; 727 entry.set = left.set[i]; 728 entry.tag = left.tag[i]; 729 entry.metered = left.metered[i]; 730 entry.roaming = left.roaming[i]; 731 entry.defaultNetwork = left.defaultNetwork[i]; 732 entry.rxBytes = left.rxBytes[i]; 733 entry.rxPackets = left.rxPackets[i]; 734 entry.txBytes = left.txBytes[i]; 735 entry.txPackets = left.txPackets[i]; 736 entry.operations = left.operations[i]; 737 738 // find remote row that matches, and subtract 739 final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, 740 entry.metered, entry.roaming, entry.defaultNetwork, i); 741 if (j != -1) { 742 // Found matching row, subtract remote value. 743 entry.rxBytes -= right.rxBytes[j]; 744 entry.rxPackets -= right.rxPackets[j]; 745 entry.txBytes -= right.txBytes[j]; 746 entry.txPackets -= right.txPackets[j]; 747 entry.operations -= right.operations[j]; 748 } 749 750 if (entry.isNegative()) { 751 if (observer != null) { 752 observer.foundNonMonotonic(left, i, right, j, cookie); 753 } 754 entry.rxBytes = Math.max(entry.rxBytes, 0); 755 entry.rxPackets = Math.max(entry.rxPackets, 0); 756 entry.txBytes = Math.max(entry.txBytes, 0); 757 entry.txPackets = Math.max(entry.txPackets, 0); 758 entry.operations = Math.max(entry.operations, 0); 759 } 760 761 result.addValues(entry); 762 } 763 764 return result; 765 } 766 767 /** 768 * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice. 769 * 770 * <p>This mutates both base and stacked traffic stats, to account respectively for 771 * double-counted traffic and IPv4/IPv6 header size difference. 772 * 773 * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4 774 * packet on the stacked interface, and once as translated to an IPv6 packet on the 775 * base interface. For correct stats accounting on the base interface, every 464xlat 776 * packet needs to be subtracted from the root UID on the base interface both for tx 777 * and rx traffic (http://b/12249687, http:/b/33681750). 778 * 779 * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only 780 * {@code ConcurrentHashMap} 781 * @param baseTraffic Traffic on the base interfaces. Will be mutated. 782 * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated. 783 * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both. 784 */ apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic, Map<String, String> stackedIfaces)785 public static void apply464xlatAdjustments(NetworkStats baseTraffic, 786 NetworkStats stackedTraffic, Map<String, String> stackedIfaces) { 787 // Total 464xlat traffic to subtract from uid 0 on all base interfaces. 788 // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically. 789 final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size()); 790 791 // For recycling 792 Entry entry = null; 793 Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L); 794 795 for (int i = 0; i < stackedTraffic.size; i++) { 796 entry = stackedTraffic.getValues(i, entry); 797 if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) { 798 continue; 799 } 800 final String baseIface = stackedIfaces.get(entry.iface); 801 if (baseIface == null) { 802 continue; 803 } 804 // Subtract any 464lat traffic seen for the root UID on the current base interface. 805 adjust.iface = baseIface; 806 adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA); 807 adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA); 808 adjust.rxPackets = -entry.rxPackets; 809 adjust.txPackets = -entry.txPackets; 810 adjustments.combineValues(adjust); 811 812 // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent 813 // on the stacked interface with prefix "v4-" and drops the IPv6 header size after 814 // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes 815 // difference for all packets (http://b/12249687, http:/b/33681750). 816 entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA; 817 entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA; 818 stackedTraffic.setValues(i, entry); 819 } 820 821 baseTraffic.combineAllValues(adjustments); 822 } 823 824 /** 825 * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice. 826 * 827 * <p>This mutates the object this method is called on. Equivalent to calling 828 * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as 829 * base and stacked traffic. 830 * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both. 831 */ apply464xlatAdjustments(Map<String, String> stackedIfaces)832 public void apply464xlatAdjustments(Map<String, String> stackedIfaces) { 833 apply464xlatAdjustments(this, this, stackedIfaces); 834 } 835 836 /** 837 * Return total statistics grouped by {@link #iface}; doesn't mutate the 838 * original structure. 839 */ groupedByIface()840 public NetworkStats groupedByIface() { 841 final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); 842 843 final Entry entry = new Entry(); 844 entry.uid = UID_ALL; 845 entry.set = SET_ALL; 846 entry.tag = TAG_NONE; 847 entry.metered = METERED_ALL; 848 entry.roaming = ROAMING_ALL; 849 entry.defaultNetwork = DEFAULT_NETWORK_ALL; 850 entry.operations = 0L; 851 852 for (int i = 0; i < size; i++) { 853 // skip specific tags, since already counted in TAG_NONE 854 if (tag[i] != TAG_NONE) continue; 855 856 entry.iface = iface[i]; 857 entry.rxBytes = rxBytes[i]; 858 entry.rxPackets = rxPackets[i]; 859 entry.txBytes = txBytes[i]; 860 entry.txPackets = txPackets[i]; 861 stats.combineValues(entry); 862 } 863 864 return stats; 865 } 866 867 /** 868 * Return total statistics grouped by {@link #uid}; doesn't mutate the 869 * original structure. 870 */ groupedByUid()871 public NetworkStats groupedByUid() { 872 final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); 873 874 final Entry entry = new Entry(); 875 entry.iface = IFACE_ALL; 876 entry.set = SET_ALL; 877 entry.tag = TAG_NONE; 878 entry.metered = METERED_ALL; 879 entry.roaming = ROAMING_ALL; 880 entry.defaultNetwork = DEFAULT_NETWORK_ALL; 881 882 for (int i = 0; i < size; i++) { 883 // skip specific tags, since already counted in TAG_NONE 884 if (tag[i] != TAG_NONE) continue; 885 886 entry.uid = uid[i]; 887 entry.rxBytes = rxBytes[i]; 888 entry.rxPackets = rxPackets[i]; 889 entry.txBytes = txBytes[i]; 890 entry.txPackets = txPackets[i]; 891 entry.operations = operations[i]; 892 stats.combineValues(entry); 893 } 894 895 return stats; 896 } 897 898 /** 899 * Return all rows except those attributed to the requested UID; doesn't 900 * mutate the original structure. 901 */ withoutUids(int[] uids)902 public NetworkStats withoutUids(int[] uids) { 903 final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); 904 905 Entry entry = new Entry(); 906 for (int i = 0; i < size; i++) { 907 entry = getValues(i, entry); 908 if (!ArrayUtils.contains(uids, entry.uid)) { 909 stats.addValues(entry); 910 } 911 } 912 913 return stats; 914 } 915 916 /** 917 * Only keep entries that match all specified filters. 918 * 919 * <p>This mutates the original structure in place. After this method is called, 920 * size is the number of matching entries, and capacity is the previous capacity. 921 * @param limitUid UID to filter for, or {@link #UID_ALL}. 922 * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}. 923 * @param limitTag Tag to filter for, or {@link #TAG_ALL}. 924 */ filter(int limitUid, String[] limitIfaces, int limitTag)925 public void filter(int limitUid, String[] limitIfaces, int limitTag) { 926 if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) { 927 return; 928 } 929 930 Entry entry = new Entry(); 931 int nextOutputEntry = 0; 932 for (int i = 0; i < size; i++) { 933 entry = getValues(i, entry); 934 final boolean matches = 935 (limitUid == UID_ALL || limitUid == entry.uid) 936 && (limitTag == TAG_ALL || limitTag == entry.tag) 937 && (limitIfaces == INTERFACES_ALL 938 || ArrayUtils.contains(limitIfaces, entry.iface)); 939 940 if (matches) { 941 setValues(nextOutputEntry, entry); 942 nextOutputEntry++; 943 } 944 } 945 946 size = nextOutputEntry; 947 } 948 dump(String prefix, PrintWriter pw)949 public void dump(String prefix, PrintWriter pw) { 950 pw.print(prefix); 951 pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); 952 for (int i = 0; i < size; i++) { 953 pw.print(prefix); 954 pw.print(" ["); pw.print(i); pw.print("]"); 955 pw.print(" iface="); pw.print(iface[i]); 956 pw.print(" uid="); pw.print(uid[i]); 957 pw.print(" set="); pw.print(setToString(set[i])); 958 pw.print(" tag="); pw.print(tagToString(tag[i])); 959 pw.print(" metered="); pw.print(meteredToString(metered[i])); 960 pw.print(" roaming="); pw.print(roamingToString(roaming[i])); 961 pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i])); 962 pw.print(" rxBytes="); pw.print(rxBytes[i]); 963 pw.print(" rxPackets="); pw.print(rxPackets[i]); 964 pw.print(" txBytes="); pw.print(txBytes[i]); 965 pw.print(" txPackets="); pw.print(txPackets[i]); 966 pw.print(" operations="); pw.println(operations[i]); 967 } 968 } 969 970 /** 971 * Return text description of {@link #set} value. 972 */ setToString(int set)973 public static String setToString(int set) { 974 switch (set) { 975 case SET_ALL: 976 return "ALL"; 977 case SET_DEFAULT: 978 return "DEFAULT"; 979 case SET_FOREGROUND: 980 return "FOREGROUND"; 981 case SET_DBG_VPN_IN: 982 return "DBG_VPN_IN"; 983 case SET_DBG_VPN_OUT: 984 return "DBG_VPN_OUT"; 985 default: 986 return "UNKNOWN"; 987 } 988 } 989 990 /** 991 * Return text description of {@link #set} value. 992 */ setToCheckinString(int set)993 public static String setToCheckinString(int set) { 994 switch (set) { 995 case SET_ALL: 996 return "all"; 997 case SET_DEFAULT: 998 return "def"; 999 case SET_FOREGROUND: 1000 return "fg"; 1001 case SET_DBG_VPN_IN: 1002 return "vpnin"; 1003 case SET_DBG_VPN_OUT: 1004 return "vpnout"; 1005 default: 1006 return "unk"; 1007 } 1008 } 1009 1010 /** 1011 * @return true if the querySet matches the dataSet. 1012 */ setMatches(int querySet, int dataSet)1013 public static boolean setMatches(int querySet, int dataSet) { 1014 if (querySet == dataSet) { 1015 return true; 1016 } 1017 // SET_ALL matches all non-debugging sets. 1018 return querySet == SET_ALL && dataSet < SET_DEBUG_START; 1019 } 1020 1021 /** 1022 * Return text description of {@link #tag} value. 1023 */ tagToString(int tag)1024 public static String tagToString(int tag) { 1025 return "0x" + Integer.toHexString(tag); 1026 } 1027 1028 /** 1029 * Return text description of {@link #metered} value. 1030 */ meteredToString(int metered)1031 public static String meteredToString(int metered) { 1032 switch (metered) { 1033 case METERED_ALL: 1034 return "ALL"; 1035 case METERED_NO: 1036 return "NO"; 1037 case METERED_YES: 1038 return "YES"; 1039 default: 1040 return "UNKNOWN"; 1041 } 1042 } 1043 1044 /** 1045 * Return text description of {@link #roaming} value. 1046 */ roamingToString(int roaming)1047 public static String roamingToString(int roaming) { 1048 switch (roaming) { 1049 case ROAMING_ALL: 1050 return "ALL"; 1051 case ROAMING_NO: 1052 return "NO"; 1053 case ROAMING_YES: 1054 return "YES"; 1055 default: 1056 return "UNKNOWN"; 1057 } 1058 } 1059 1060 /** 1061 * Return text description of {@link #defaultNetwork} value. 1062 */ defaultNetworkToString(int defaultNetwork)1063 public static String defaultNetworkToString(int defaultNetwork) { 1064 switch (defaultNetwork) { 1065 case DEFAULT_NETWORK_ALL: 1066 return "ALL"; 1067 case DEFAULT_NETWORK_NO: 1068 return "NO"; 1069 case DEFAULT_NETWORK_YES: 1070 return "YES"; 1071 default: 1072 return "UNKNOWN"; 1073 } 1074 } 1075 1076 @Override toString()1077 public String toString() { 1078 final CharArrayWriter writer = new CharArrayWriter(); 1079 dump("", new PrintWriter(writer)); 1080 return writer.toString(); 1081 } 1082 1083 @Override describeContents()1084 public int describeContents() { 1085 return 0; 1086 } 1087 1088 public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { 1089 @Override 1090 public NetworkStats createFromParcel(Parcel in) { 1091 return new NetworkStats(in); 1092 } 1093 1094 @Override 1095 public NetworkStats[] newArray(int size) { 1096 return new NetworkStats[size]; 1097 } 1098 }; 1099 1100 public interface NonMonotonicObserver<C> { foundNonMonotonic( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie)1101 public void foundNonMonotonic( 1102 NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie); foundNonMonotonic( NetworkStats stats, int statsIndex, C cookie)1103 public void foundNonMonotonic( 1104 NetworkStats stats, int statsIndex, C cookie); 1105 } 1106 1107 /** 1108 * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface. 1109 * 1110 * This method should only be called on delta NetworkStats. Do not call this method on a 1111 * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may 1112 * change over time. 1113 * 1114 * This method performs adjustments for one active VPN package and one VPN iface at a time. 1115 * 1116 * It is possible for the VPN software to use multiple underlying networks. This method 1117 * only migrates traffic for the primary underlying network. 1118 * 1119 * @param tunUid uid of the VPN application 1120 * @param tunIface iface of the vpn tunnel 1121 * @param underlyingIface the primary underlying network iface used by the VPN application 1122 * @return true if it successfully adjusts the accounting for VPN, false otherwise 1123 */ migrateTun(int tunUid, String tunIface, String underlyingIface)1124 public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) { 1125 Entry tunIfaceTotal = new Entry(); 1126 Entry underlyingIfaceTotal = new Entry(); 1127 1128 tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal); 1129 1130 // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app. 1131 // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression. 1132 // Negative stats should be avoided. 1133 Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal); 1134 if (pool.isEmpty()) { 1135 return true; 1136 } 1137 Entry moved = 1138 addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool); 1139 deductTrafficFromVpnApp(tunUid, underlyingIface, moved); 1140 1141 if (!moved.isEmpty()) { 1142 Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved=" 1143 + moved); 1144 return false; 1145 } 1146 return true; 1147 } 1148 1149 /** 1150 * Initializes the data used by the migrateTun() method. 1151 * 1152 * This is the first pass iteration which does the following work: 1153 * (1) Adds up all the traffic through the tunUid's underlyingIface 1154 * (both foreground and background). 1155 * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself. 1156 */ tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface, Entry tunIfaceTotal, Entry underlyingIfaceTotal)1157 private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface, 1158 Entry tunIfaceTotal, Entry underlyingIfaceTotal) { 1159 Entry recycle = new Entry(); 1160 for (int i = 0; i < size; i++) { 1161 getValues(i, recycle); 1162 if (recycle.uid == UID_ALL) { 1163 throw new IllegalStateException( 1164 "Cannot adjust VPN accounting on an iface aggregated NetworkStats."); 1165 } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) { 1166 throw new IllegalStateException( 1167 "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*"); 1168 } 1169 1170 if (recycle.uid == tunUid && recycle.tag == TAG_NONE 1171 && Objects.equals(underlyingIface, recycle.iface)) { 1172 underlyingIfaceTotal.add(recycle); 1173 } 1174 1175 if (recycle.uid != tunUid && recycle.tag == TAG_NONE 1176 && Objects.equals(tunIface, recycle.iface)) { 1177 // Add up all tunIface traffic excluding traffic from the vpn app itself. 1178 tunIfaceTotal.add(recycle); 1179 } 1180 } 1181 } 1182 tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal)1183 private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) { 1184 Entry pool = new Entry(); 1185 pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes); 1186 pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets); 1187 pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes); 1188 pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets); 1189 pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations); 1190 return pool; 1191 } 1192 addTrafficToApplications(int tunUid, String tunIface, String underlyingIface, Entry tunIfaceTotal, Entry pool)1193 private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface, 1194 Entry tunIfaceTotal, Entry pool) { 1195 Entry moved = new Entry(); 1196 Entry tmpEntry = new Entry(); 1197 tmpEntry.iface = underlyingIface; 1198 for (int i = 0; i < size; i++) { 1199 // the vpn app is excluded from the redistribution but all moved traffic will be 1200 // deducted from the vpn app (see deductTrafficFromVpnApp below). 1201 if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) { 1202 if (tunIfaceTotal.rxBytes > 0) { 1203 tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; 1204 } else { 1205 tmpEntry.rxBytes = 0; 1206 } 1207 if (tunIfaceTotal.rxPackets > 0) { 1208 tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; 1209 } else { 1210 tmpEntry.rxPackets = 0; 1211 } 1212 if (tunIfaceTotal.txBytes > 0) { 1213 tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes; 1214 } else { 1215 tmpEntry.txBytes = 0; 1216 } 1217 if (tunIfaceTotal.txPackets > 0) { 1218 tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets; 1219 } else { 1220 tmpEntry.txPackets = 0; 1221 } 1222 if (tunIfaceTotal.operations > 0) { 1223 tmpEntry.operations = 1224 pool.operations * operations[i] / tunIfaceTotal.operations; 1225 } else { 1226 tmpEntry.operations = 0; 1227 } 1228 tmpEntry.uid = uid[i]; 1229 tmpEntry.tag = tag[i]; 1230 tmpEntry.set = set[i]; 1231 tmpEntry.metered = metered[i]; 1232 tmpEntry.roaming = roaming[i]; 1233 tmpEntry.defaultNetwork = defaultNetwork[i]; 1234 combineValues(tmpEntry); 1235 if (tag[i] == TAG_NONE) { 1236 moved.add(tmpEntry); 1237 // Add debug info 1238 tmpEntry.set = SET_DBG_VPN_IN; 1239 combineValues(tmpEntry); 1240 } 1241 } 1242 } 1243 return moved; 1244 } 1245 deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved)1246 private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) { 1247 // Add debug info 1248 moved.uid = tunUid; 1249 moved.set = SET_DBG_VPN_OUT; 1250 moved.tag = TAG_NONE; 1251 moved.iface = underlyingIface; 1252 moved.metered = METERED_ALL; 1253 moved.roaming = ROAMING_ALL; 1254 moved.defaultNetwork = DEFAULT_NETWORK_ALL; 1255 combineValues(moved); 1256 1257 // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than 1258 // the TAG_NONE traffic. 1259 // 1260 // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO, 1261 // which should be the case as it comes directly from the /proc file. We only blend in the 1262 // roaming data after applying these adjustments, by checking the NetworkIdentity of the 1263 // underlying iface. 1264 int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, 1265 METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); 1266 if (idxVpnBackground != -1) { 1267 tunSubtract(idxVpnBackground, this, moved); 1268 } 1269 1270 int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 1271 METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); 1272 if (idxVpnForeground != -1) { 1273 tunSubtract(idxVpnForeground, this, moved); 1274 } 1275 } 1276 tunSubtract(int i, NetworkStats left, Entry right)1277 private static void tunSubtract(int i, NetworkStats left, Entry right) { 1278 long rxBytes = Math.min(left.rxBytes[i], right.rxBytes); 1279 left.rxBytes[i] -= rxBytes; 1280 right.rxBytes -= rxBytes; 1281 1282 long rxPackets = Math.min(left.rxPackets[i], right.rxPackets); 1283 left.rxPackets[i] -= rxPackets; 1284 right.rxPackets -= rxPackets; 1285 1286 long txBytes = Math.min(left.txBytes[i], right.txBytes); 1287 left.txBytes[i] -= txBytes; 1288 right.txBytes -= txBytes; 1289 1290 long txPackets = Math.min(left.txPackets[i], right.txPackets); 1291 left.txPackets[i] -= txPackets; 1292 right.txPackets -= txPackets; 1293 } 1294 } 1295