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