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