NetworkStats.java revision 6dba3936a2f8beacfa44e86265f3095d060b7b9d
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     * 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 */
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
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
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
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
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
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
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
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
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
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         */
354        public int getUid() {
355            return mUid;
356        }
357
358        /**
359         * Tag of the bucket.<p />
360         * @return Bucket tag.
361         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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
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     */
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
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     */
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
685    private boolean isUidEnumeration() {
686        return mUids != null;
687    }
688
689    private boolean hasNextUid() {
690        return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
691    }
692
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
706    private void setSingleUidTagState(int uid, int tag, int state) {
707        mUidOrUidIndex = uid;
708        mTag = tag;
709        mState = state;
710    }
711
712    private void stepUid() {
713        if (mUids != null) {
714            ++mUidOrUidIndex;
715        }
716    }
717}
718