[go: nahoru, domu]

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