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