1 /** 2 * Copyright (C) 2015 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.app.usage; 18 19 import android.annotation.IntDef; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.net.INetworkStatsService; 23 import android.net.INetworkStatsSession; 24 import android.net.NetworkStatsHistory; 25 import android.net.NetworkTemplate; 26 import android.net.TrafficStats; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import com.android.net.module.util.CollectionUtils; 31 32 import dalvik.system.CloseGuard; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.ArrayList; 37 38 /** 39 * Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} 40 * objects are returned as results to various queries in {@link NetworkStatsManager}. 41 */ 42 public final class NetworkStats implements AutoCloseable { 43 private static final String TAG = "NetworkStats"; 44 45 private final CloseGuard mCloseGuard = CloseGuard.get(); 46 47 /** 48 * Start timestamp of stats collected 49 */ 50 private final long mStartTimeStamp; 51 52 /** 53 * End timestamp of stats collected 54 */ 55 private final long mEndTimeStamp; 56 57 /** 58 * Non-null array indicates the query enumerates over uids. 59 */ 60 private int[] mUids; 61 62 /** 63 * Index of the current uid in mUids when doing uid enumeration or a single uid value, 64 * depending on query type. 65 */ 66 private int mUidOrUidIndex; 67 68 /** 69 * Tag id in case if was specified in the query. 70 */ 71 private int mTag = android.net.NetworkStats.TAG_NONE; 72 73 /** 74 * State in case it was not specified in the query. 75 */ 76 private int mState = Bucket.STATE_ALL; 77 78 /** 79 * The session while the query requires it, null if all the stats have been collected or close() 80 * has been called. 81 */ 82 private INetworkStatsSession mSession; 83 private NetworkTemplate mTemplate; 84 85 /** 86 * Results of a summary query. 87 */ 88 private android.net.NetworkStats mSummary = null; 89 90 /** 91 * Results of detail queries. 92 */ 93 private NetworkStatsHistory mHistory = null; 94 95 /** 96 * Where we are in enumerating over the current result. 97 */ 98 private int mEnumerationIndex = 0; 99 100 /** 101 * Recycling entry objects to prevent heap fragmentation. 102 */ 103 private android.net.NetworkStats.Entry mRecycledSummaryEntry = null; 104 private NetworkStatsHistory.Entry mRecycledHistoryEntry = null; 105 106 /** @hide */ NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp, long endTimestamp, INetworkStatsService statsService)107 NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp, 108 long endTimestamp, INetworkStatsService statsService) 109 throws RemoteException, SecurityException { 110 // Open network stats session 111 mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName()); 112 mCloseGuard.open("close"); 113 mTemplate = template; 114 mStartTimeStamp = startTimestamp; 115 mEndTimeStamp = endTimestamp; 116 } 117 118 @Override finalize()119 protected void finalize() throws Throwable { 120 try { 121 if (mCloseGuard != null) { 122 mCloseGuard.warnIfOpen(); 123 } 124 close(); 125 } finally { 126 super.finalize(); 127 } 128 } 129 130 // -------------------------BEGINNING OF PUBLIC API----------------------------------- 131 132 /** 133 * Buckets are the smallest elements of a query result. As some dimensions of a result may be 134 * aggregated (e.g. time or state) some values may be equal across all buckets. 135 */ 136 public static class Bucket { 137 /** @hide */ 138 @IntDef(prefix = { "STATE_" }, value = { 139 STATE_ALL, 140 STATE_DEFAULT, 141 STATE_FOREGROUND 142 }) 143 @Retention(RetentionPolicy.SOURCE) 144 public @interface State {} 145 146 /** 147 * Combined usage across all states. 148 */ 149 public static final int STATE_ALL = -1; 150 151 /** 152 * Usage not accounted for in any other state. 153 */ 154 public static final int STATE_DEFAULT = 0x1; 155 156 /** 157 * Foreground usage. 158 */ 159 public static final int STATE_FOREGROUND = 0x2; 160 161 /** 162 * Special UID value for aggregate/unspecified. 163 */ 164 public static final int UID_ALL = android.net.NetworkStats.UID_ALL; 165 166 /** 167 * Special UID value for removed apps. 168 */ 169 public static final int UID_REMOVED = TrafficStats.UID_REMOVED; 170 171 /** 172 * Special UID value for data usage by tethering. 173 */ 174 public static final int UID_TETHERING = TrafficStats.UID_TETHERING; 175 176 /** @hide */ 177 @IntDef(prefix = { "METERED_" }, value = { 178 METERED_ALL, 179 METERED_NO, 180 METERED_YES 181 }) 182 @Retention(RetentionPolicy.SOURCE) 183 public @interface Metered {} 184 185 /** 186 * Combined usage across all metered states. Covers metered and unmetered usage. 187 */ 188 public static final int METERED_ALL = -1; 189 190 /** 191 * Usage that occurs on an unmetered network. 192 */ 193 public static final int METERED_NO = 0x1; 194 195 /** 196 * Usage that occurs on a metered network. 197 * 198 * <p>A network is classified as metered when the user is sensitive to heavy data usage on 199 * that connection. 200 */ 201 public static final int METERED_YES = 0x2; 202 203 /** @hide */ 204 @IntDef(prefix = { "ROAMING_" }, value = { 205 ROAMING_ALL, 206 ROAMING_NO, 207 ROAMING_YES 208 }) 209 @Retention(RetentionPolicy.SOURCE) 210 public @interface Roaming {} 211 212 /** 213 * Combined usage across all roaming states. Covers both roaming and non-roaming usage. 214 */ 215 public static final int ROAMING_ALL = -1; 216 217 /** 218 * Usage that occurs on a home, non-roaming network. 219 * 220 * <p>Any cellular usage in this bucket was incurred while the device was connected to a 221 * tower owned or operated by the user's wireless carrier, or a tower that the user's 222 * wireless carrier has indicated should be treated as a home network regardless. 223 * 224 * <p>This is also the default value for network types that do not support roaming. 225 */ 226 public static final int ROAMING_NO = 0x1; 227 228 /** 229 * Usage that occurs on a roaming network. 230 * 231 * <p>Any cellular usage in this bucket as incurred while the device was roaming on another 232 * carrier's network, for which additional charges may apply. 233 */ 234 public static final int ROAMING_YES = 0x2; 235 236 /** @hide */ 237 @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = { 238 DEFAULT_NETWORK_ALL, 239 DEFAULT_NETWORK_NO, 240 DEFAULT_NETWORK_YES 241 }) 242 @Retention(RetentionPolicy.SOURCE) 243 public @interface DefaultNetworkStatus {} 244 245 /** 246 * Combined usage for this network regardless of default network status. 247 */ 248 public static final int DEFAULT_NETWORK_ALL = -1; 249 250 /** 251 * Usage that occurs while this network is not a default network. 252 * 253 * <p>This implies that the app responsible for this usage requested that it occur on a 254 * specific network different from the one(s) the system would have selected for it. 255 */ 256 public static final int DEFAULT_NETWORK_NO = 0x1; 257 258 /** 259 * Usage that occurs while this network is a default network. 260 * 261 * <p>This implies that the app either did not select a specific network for this usage, 262 * or it selected a network that the system could have selected for app traffic. 263 */ 264 public static final int DEFAULT_NETWORK_YES = 0x2; 265 266 /** 267 * Special TAG value for total data across all tags 268 */ 269 public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE; 270 271 private int mUid; 272 private int mTag; 273 private int mState; 274 private int mDefaultNetworkStatus; 275 private int mMetered; 276 private int mRoaming; 277 private long mBeginTimeStamp; 278 private long mEndTimeStamp; 279 private long mRxBytes; 280 private long mRxPackets; 281 private long mTxBytes; 282 private long mTxPackets; 283 convertSet(@tate int state)284 private static int convertSet(@State int state) { 285 switch (state) { 286 case STATE_ALL: return android.net.NetworkStats.SET_ALL; 287 case STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT; 288 case STATE_FOREGROUND: return android.net.NetworkStats.SET_FOREGROUND; 289 } 290 return 0; 291 } 292 convertState(int networkStatsSet)293 private static @State int convertState(int networkStatsSet) { 294 switch (networkStatsSet) { 295 case android.net.NetworkStats.SET_ALL : return STATE_ALL; 296 case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT; 297 case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND; 298 } 299 return 0; 300 } 301 convertUid(int uid)302 private static int convertUid(int uid) { 303 switch (uid) { 304 case TrafficStats.UID_REMOVED: return UID_REMOVED; 305 case TrafficStats.UID_TETHERING: return UID_TETHERING; 306 } 307 return uid; 308 } 309 convertTag(int tag)310 private static int convertTag(int tag) { 311 switch (tag) { 312 case android.net.NetworkStats.TAG_NONE: return TAG_NONE; 313 } 314 return tag; 315 } 316 convertMetered(int metered)317 private static @Metered int convertMetered(int metered) { 318 switch (metered) { 319 case android.net.NetworkStats.METERED_ALL : return METERED_ALL; 320 case android.net.NetworkStats.METERED_NO: return METERED_NO; 321 case android.net.NetworkStats.METERED_YES: return METERED_YES; 322 } 323 return 0; 324 } 325 convertRoaming(int roaming)326 private static @Roaming int convertRoaming(int roaming) { 327 switch (roaming) { 328 case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL; 329 case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO; 330 case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES; 331 } 332 return 0; 333 } 334 convertDefaultNetworkStatus( int defaultNetworkStatus)335 private static @DefaultNetworkStatus int convertDefaultNetworkStatus( 336 int defaultNetworkStatus) { 337 switch (defaultNetworkStatus) { 338 case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL; 339 case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO; 340 case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES; 341 } 342 return 0; 343 } 344 Bucket()345 public Bucket() { 346 } 347 348 /** 349 * Key of the bucket. Usually an app uid or one of the following special values:<p /> 350 * <ul> 351 * <li>{@link #UID_REMOVED}</li> 352 * <li>{@link #UID_TETHERING}</li> 353 * <li>{@link android.os.Process#SYSTEM_UID}</li> 354 * </ul> 355 * @return Bucket key. 356 */ getUid()357 public int getUid() { 358 return mUid; 359 } 360 361 /** 362 * Tag of the bucket.<p /> 363 * @return Bucket tag. 364 */ getTag()365 public int getTag() { 366 return mTag; 367 } 368 369 /** 370 * Usage state. One of the following values:<p/> 371 * <ul> 372 * <li>{@link #STATE_ALL}</li> 373 * <li>{@link #STATE_DEFAULT}</li> 374 * <li>{@link #STATE_FOREGROUND}</li> 375 * </ul> 376 * @return Usage state. 377 */ getState()378 public @State int getState() { 379 return mState; 380 } 381 382 /** 383 * Metered state. One of the following values:<p/> 384 * <ul> 385 * <li>{@link #METERED_ALL}</li> 386 * <li>{@link #METERED_NO}</li> 387 * <li>{@link #METERED_YES}</li> 388 * </ul> 389 * <p>A network is classified as metered when the user is sensitive to heavy data usage on 390 * that connection. Apps may warn before using these networks for large downloads. The 391 * metered state can be set by the user within data usage network restrictions. 392 */ getMetered()393 public @Metered int getMetered() { 394 return mMetered; 395 } 396 397 /** 398 * Roaming state. One of the following values:<p/> 399 * <ul> 400 * <li>{@link #ROAMING_ALL}</li> 401 * <li>{@link #ROAMING_NO}</li> 402 * <li>{@link #ROAMING_YES}</li> 403 * </ul> 404 */ getRoaming()405 public @Roaming int getRoaming() { 406 return mRoaming; 407 } 408 409 /** 410 * Default network status. One of the following values:<p/> 411 * <ul> 412 * <li>{@link #DEFAULT_NETWORK_ALL}</li> 413 * <li>{@link #DEFAULT_NETWORK_NO}</li> 414 * <li>{@link #DEFAULT_NETWORK_YES}</li> 415 * </ul> 416 */ getDefaultNetworkStatus()417 public @DefaultNetworkStatus int getDefaultNetworkStatus() { 418 return mDefaultNetworkStatus; 419 } 420 421 /** 422 * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see 423 * {@link java.lang.System#currentTimeMillis}. 424 * @return Start of interval. 425 */ getStartTimeStamp()426 public long getStartTimeStamp() { 427 return mBeginTimeStamp; 428 } 429 430 /** 431 * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see 432 * {@link java.lang.System#currentTimeMillis}. 433 * @return End of interval. 434 */ getEndTimeStamp()435 public long getEndTimeStamp() { 436 return mEndTimeStamp; 437 } 438 439 /** 440 * Number of bytes received during the bucket's time interval. Statistics are measured at 441 * the network layer, so they include both TCP and UDP usage. 442 * @return Number of bytes. 443 */ getRxBytes()444 public long getRxBytes() { 445 return mRxBytes; 446 } 447 448 /** 449 * Number of bytes transmitted during the bucket's time interval. Statistics are measured at 450 * the network layer, so they include both TCP and UDP usage. 451 * @return Number of bytes. 452 */ getTxBytes()453 public long getTxBytes() { 454 return mTxBytes; 455 } 456 457 /** 458 * Number of packets received during the bucket's time interval. Statistics are measured at 459 * the network layer, so they include both TCP and UDP usage. 460 * @return Number of packets. 461 */ getRxPackets()462 public long getRxPackets() { 463 return mRxPackets; 464 } 465 466 /** 467 * Number of packets transmitted during the bucket's time interval. Statistics are measured 468 * at the network layer, so they include both TCP and UDP usage. 469 * @return Number of packets. 470 */ getTxPackets()471 public long getTxPackets() { 472 return mTxPackets; 473 } 474 } 475 476 /** 477 * Fills the recycled bucket with data of the next bin in the enumeration. 478 * @param bucketOut Bucket to be filled with data. If null, the method does 479 * nothing and returning false. 480 * @return true if successfully filled the bucket, false otherwise. 481 */ getNextBucket(@ullable Bucket bucketOut)482 public boolean getNextBucket(@Nullable Bucket bucketOut) { 483 if (mSummary != null) { 484 return getNextSummaryBucket(bucketOut); 485 } else { 486 return getNextHistoryBucket(bucketOut); 487 } 488 } 489 490 /** 491 * Check if it is possible to ask for a next bucket in the enumeration. 492 * @return true if there is at least one more bucket. 493 */ hasNextBucket()494 public boolean hasNextBucket() { 495 if (mSummary != null) { 496 return mEnumerationIndex < mSummary.size(); 497 } else if (mHistory != null) { 498 return mEnumerationIndex < mHistory.size() 499 || hasNextUid(); 500 } 501 return false; 502 } 503 504 /** 505 * Closes the enumeration. Call this method before this object gets out of scope. 506 */ 507 @Override close()508 public void close() { 509 if (mSession != null) { 510 try { 511 mSession.close(); 512 } catch (RemoteException e) { 513 Log.w(TAG, e); 514 // Otherwise, meh 515 } 516 } 517 mSession = null; 518 if (mCloseGuard != null) { 519 mCloseGuard.close(); 520 } 521 } 522 523 // -------------------------END OF PUBLIC API----------------------------------- 524 525 /** 526 * Collects device summary results into a Bucket. 527 * @throws RemoteException 528 */ getDeviceSummaryForNetwork()529 Bucket getDeviceSummaryForNetwork() throws RemoteException { 530 mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp); 531 532 // Setting enumeration index beyond end to avoid accidental enumeration over data that does 533 // not belong to the calling user. 534 mEnumerationIndex = mSummary.size(); 535 536 return getSummaryAggregate(); 537 } 538 539 /** 540 * Collects summary results and sets summary enumeration mode. 541 * @throws RemoteException 542 */ startSummaryEnumeration()543 void startSummaryEnumeration() throws RemoteException { 544 mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp, 545 false /* includeTags */); 546 mEnumerationIndex = 0; 547 } 548 549 /** 550 * Collects tagged summary results and sets summary enumeration mode. 551 * @throws RemoteException 552 */ startTaggedSummaryEnumeration()553 void startTaggedSummaryEnumeration() throws RemoteException { 554 mSummary = mSession.getTaggedSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp); 555 mEnumerationIndex = 0; 556 } 557 558 /** 559 * Collects history results for uid and resets history enumeration index. 560 */ startHistoryUidEnumeration(int uid, int tag, int state)561 void startHistoryUidEnumeration(int uid, int tag, int state) { 562 mHistory = null; 563 try { 564 mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid, 565 Bucket.convertSet(state), tag, NetworkStatsHistory.FIELD_ALL, 566 mStartTimeStamp, mEndTimeStamp); 567 setSingleUidTagState(uid, tag, state); 568 } catch (RemoteException e) { 569 Log.w(TAG, e); 570 // Leaving mHistory null 571 } 572 mEnumerationIndex = 0; 573 } 574 575 /** 576 * Collects history results for network and resets history enumeration index. 577 */ startHistoryDeviceEnumeration()578 void startHistoryDeviceEnumeration() { 579 try { 580 mHistory = mSession.getHistoryIntervalForNetwork( 581 mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); 582 } catch (RemoteException e) { 583 Log.w(TAG, e); 584 mHistory = null; 585 } 586 mEnumerationIndex = 0; 587 } 588 589 /** 590 * Starts uid enumeration for current user. 591 * @throws RemoteException 592 */ startUserUidEnumeration()593 void startUserUidEnumeration() throws RemoteException { 594 // TODO: getRelevantUids should be sensitive to time interval. When that's done, 595 // the filtering logic below can be removed. 596 int[] uids = mSession.getRelevantUids(); 597 // Filtering of uids with empty history. 598 final ArrayList<Integer> filteredUids = new ArrayList<>(); 599 for (int uid : uids) { 600 try { 601 NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid, 602 android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE, 603 NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); 604 if (history != null && history.size() > 0) { 605 filteredUids.add(uid); 606 } 607 } catch (RemoteException e) { 608 Log.w(TAG, "Error while getting history of uid " + uid, e); 609 } 610 } 611 mUids = CollectionUtils.toIntArray(filteredUids); 612 mUidOrUidIndex = -1; 613 stepHistory(); 614 } 615 616 /** 617 * Steps to next uid in enumeration and collects history for that. 618 */ stepHistory()619 private void stepHistory() { 620 if (hasNextUid()) { 621 stepUid(); 622 mHistory = null; 623 try { 624 mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(), 625 android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE, 626 NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); 627 } catch (RemoteException e) { 628 Log.w(TAG, e); 629 // Leaving mHistory null 630 } 631 mEnumerationIndex = 0; 632 } 633 } 634 fillBucketFromSummaryEntry(Bucket bucketOut)635 private void fillBucketFromSummaryEntry(Bucket bucketOut) { 636 bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid); 637 bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag); 638 bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set); 639 bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus( 640 mRecycledSummaryEntry.defaultNetwork); 641 bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered); 642 bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming); 643 bucketOut.mBeginTimeStamp = mStartTimeStamp; 644 bucketOut.mEndTimeStamp = mEndTimeStamp; 645 bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes; 646 bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets; 647 bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes; 648 bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets; 649 } 650 651 /** 652 * Getting the next item in summary enumeration. 653 * @param bucketOut Next item will be set here. 654 * @return true if a next item could be set. 655 */ getNextSummaryBucket(@ullable Bucket bucketOut)656 private boolean getNextSummaryBucket(@Nullable Bucket bucketOut) { 657 if (bucketOut != null && mEnumerationIndex < mSummary.size()) { 658 mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry); 659 fillBucketFromSummaryEntry(bucketOut); 660 return true; 661 } 662 return false; 663 } 664 getSummaryAggregate()665 Bucket getSummaryAggregate() { 666 if (mSummary == null) { 667 return null; 668 } 669 Bucket bucket = new Bucket(); 670 if (mRecycledSummaryEntry == null) { 671 mRecycledSummaryEntry = new android.net.NetworkStats.Entry(); 672 } 673 mSummary.getTotal(mRecycledSummaryEntry); 674 fillBucketFromSummaryEntry(bucket); 675 return bucket; 676 } 677 678 /** 679 * Getting the next item in a history enumeration. 680 * @param bucketOut Next item will be set here. 681 * @return true if a next item could be set. 682 */ getNextHistoryBucket(@ullable Bucket bucketOut)683 private boolean getNextHistoryBucket(@Nullable Bucket bucketOut) { 684 if (bucketOut != null && mHistory != null) { 685 if (mEnumerationIndex < mHistory.size()) { 686 mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++, 687 mRecycledHistoryEntry); 688 bucketOut.mUid = Bucket.convertUid(getUid()); 689 bucketOut.mTag = Bucket.convertTag(mTag); 690 bucketOut.mState = mState; 691 bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL; 692 bucketOut.mMetered = Bucket.METERED_ALL; 693 bucketOut.mRoaming = Bucket.ROAMING_ALL; 694 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart; 695 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart 696 + mRecycledHistoryEntry.bucketDuration; 697 bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes; 698 bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets; 699 bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes; 700 bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets; 701 return true; 702 } else if (hasNextUid()) { 703 stepHistory(); 704 return getNextHistoryBucket(bucketOut); 705 } 706 } 707 return false; 708 } 709 710 // ------------------ UID LOGIC------------------------ 711 isUidEnumeration()712 private boolean isUidEnumeration() { 713 return mUids != null; 714 } 715 hasNextUid()716 private boolean hasNextUid() { 717 return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length; 718 } 719 getUid()720 private int getUid() { 721 // Check if uid enumeration. 722 if (isUidEnumeration()) { 723 if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) { 724 throw new IndexOutOfBoundsException( 725 "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length); 726 } 727 return mUids[mUidOrUidIndex]; 728 } 729 // Single uid mode. 730 return mUidOrUidIndex; 731 } 732 setSingleUidTagState(int uid, int tag, int state)733 private void setSingleUidTagState(int uid, int tag, int state) { 734 mUidOrUidIndex = uid; 735 mTag = tag; 736 mState = state; 737 } 738 stepUid()739 private void stepUid() { 740 if (mUids != null) { 741 ++mUidOrUidIndex; 742 } 743 } 744 } 745