• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package android.app.usage;
18 
19 import android.annotation.IntDef;
20 import android.content.Context;
21 import android.net.INetworkStatsService;
22 import android.net.INetworkStatsSession;
23 import android.net.NetworkStatsHistory;
24 import android.net.NetworkTemplate;
25 import android.net.TrafficStats;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 import android.util.IntArray;
29 import android.util.Log;
30 
31 import dalvik.system.CloseGuard;
32 
33 import java.lang.annotation.Retention;
34 import 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  */
40 public 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 */
NetworkStats(Context context, NetworkTemplate template, long startTimestamp, long endTimestamp)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
finalize()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({METERED_ALL, METERED_NO, METERED_YES})
168         @Retention(RetentionPolicy.SOURCE)
169         public @interface Metered {}
170 
171         /**
172          * Combined usage across all metered states. Covers metered and unmetered usage.
173          */
174         public static final int METERED_ALL = -1;
175 
176         /**
177          * Usage that occurs on an unmetered network.
178          */
179         public static final int METERED_NO = 0x1;
180 
181         /**
182          * Usage that occurs on a metered network.
183          *
184          * <p>A network is classified as metered when the user is sensitive to heavy data usage on
185          * that connection.
186          */
187         public static final int METERED_YES = 0x2;
188 
189         /** @hide */
190         @IntDef({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
191         @Retention(RetentionPolicy.SOURCE)
192         public @interface Roaming {}
193 
194         /**
195          * Combined usage across all roaming states. Covers both roaming and non-roaming usage.
196          */
197         public static final int ROAMING_ALL = -1;
198 
199         /**
200          * Usage that occurs on a home, non-roaming network.
201          *
202          * <p>Any cellular usage in this bucket was incurred while the device was connected to a
203          * tower owned or operated by the user's wireless carrier, or a tower that the user's
204          * wireless carrier has indicated should be treated as a home network regardless.
205          *
206          * <p>This is also the default value for network types that do not support roaming.
207          */
208         public static final int ROAMING_NO = 0x1;
209 
210         /**
211          * Usage that occurs on a roaming network.
212          *
213          * <p>Any cellular usage in this bucket as incurred while the device was roaming on another
214          * carrier's network, for which additional charges may apply.
215          */
216         public static final int ROAMING_YES = 0x2;
217 
218         /**
219          * Special TAG value for total data across all tags
220          */
221         public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE;
222 
223         private int mUid;
224         private int mTag;
225         private int mState;
226         private int mMetered;
227         private int mRoaming;
228         private long mBeginTimeStamp;
229         private long mEndTimeStamp;
230         private long mRxBytes;
231         private long mRxPackets;
232         private long mTxBytes;
233         private long mTxPackets;
234 
convertState(int networkStatsSet)235         private static @State int convertState(int networkStatsSet) {
236             switch (networkStatsSet) {
237                 case android.net.NetworkStats.SET_ALL : return STATE_ALL;
238                 case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
239                 case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
240             }
241             return 0;
242         }
243 
convertUid(int uid)244         private static int convertUid(int uid) {
245             switch (uid) {
246                 case TrafficStats.UID_REMOVED: return UID_REMOVED;
247                 case TrafficStats.UID_TETHERING: return UID_TETHERING;
248             }
249             return uid;
250         }
251 
convertTag(int tag)252         private static int convertTag(int tag) {
253             switch (tag) {
254                 case android.net.NetworkStats.TAG_NONE: return TAG_NONE;
255             }
256             return tag;
257         }
258 
convertMetered(int metered)259         private static @Metered int convertMetered(int metered) {
260             switch (metered) {
261                 case android.net.NetworkStats.METERED_ALL : return METERED_ALL;
262                 case android.net.NetworkStats.METERED_NO: return METERED_NO;
263                 case android.net.NetworkStats.METERED_YES: return METERED_YES;
264             }
265             return 0;
266         }
267 
convertRoaming(int roaming)268         private static @Roaming int convertRoaming(int roaming) {
269             switch (roaming) {
270                 case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
271                 case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO;
272                 case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES;
273             }
274             return 0;
275         }
276 
Bucket()277         public Bucket() {
278         }
279 
280         /**
281          * Key of the bucket. Usually an app uid or one of the following special values:<p />
282          * <ul>
283          * <li>{@link #UID_REMOVED}</li>
284          * <li>{@link #UID_TETHERING}</li>
285          * <li>{@link android.os.Process#SYSTEM_UID}</li>
286          * </ul>
287          * @return Bucket key.
288          */
getUid()289         public int getUid() {
290             return mUid;
291         }
292 
293         /**
294          * Tag of the bucket.<p />
295          * @return Bucket tag.
296          */
getTag()297         public int getTag() {
298             return mTag;
299         }
300 
301         /**
302          * Usage state. One of the following values:<p/>
303          * <ul>
304          * <li>{@link #STATE_ALL}</li>
305          * <li>{@link #STATE_DEFAULT}</li>
306          * <li>{@link #STATE_FOREGROUND}</li>
307          * </ul>
308          * @return Usage state.
309          */
getState()310         public @State int getState() {
311             return mState;
312         }
313 
314         /**
315          * Metered state. One of the following values:<p/>
316          * <ul>
317          * <li>{@link #METERED_ALL}</li>
318          * <li>{@link #METERED_NO}</li>
319          * <li>{@link #METERED_YES}</li>
320          * </ul>
321          * <p>A network is classified as metered when the user is sensitive to heavy data usage on
322          * that connection. Apps may warn before using these networks for large downloads. The
323          * metered state can be set by the user within data usage network restrictions.
324          */
getMetered()325         public @Metered int getMetered() {
326             return mMetered;
327         }
328 
329         /**
330          * Roaming state. One of the following values:<p/>
331          * <ul>
332          * <li>{@link #ROAMING_ALL}</li>
333          * <li>{@link #ROAMING_NO}</li>
334          * <li>{@link #ROAMING_YES}</li>
335          * </ul>
336          */
getRoaming()337         public @Roaming int getRoaming() {
338             return mRoaming;
339         }
340 
341         /**
342          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
343          * {@link java.lang.System#currentTimeMillis}.
344          * @return Start of interval.
345          */
getStartTimeStamp()346         public long getStartTimeStamp() {
347             return mBeginTimeStamp;
348         }
349 
350         /**
351          * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
352          * {@link java.lang.System#currentTimeMillis}.
353          * @return End of interval.
354          */
getEndTimeStamp()355         public long getEndTimeStamp() {
356             return mEndTimeStamp;
357         }
358 
359         /**
360          * Number of bytes received during the bucket's time interval. Statistics are measured at
361          * the network layer, so they include both TCP and UDP usage.
362          * @return Number of bytes.
363          */
getRxBytes()364         public long getRxBytes() {
365             return mRxBytes;
366         }
367 
368         /**
369          * Number of bytes transmitted during the bucket's time interval. Statistics are measured at
370          * the network layer, so they include both TCP and UDP usage.
371          * @return Number of bytes.
372          */
getTxBytes()373         public long getTxBytes() {
374             return mTxBytes;
375         }
376 
377         /**
378          * Number of packets received during the bucket's time interval. Statistics are measured at
379          * the network layer, so they include both TCP and UDP usage.
380          * @return Number of packets.
381          */
getRxPackets()382         public long getRxPackets() {
383             return mRxPackets;
384         }
385 
386         /**
387          * Number of packets transmitted during the bucket's time interval. Statistics are measured
388          * at the network layer, so they include both TCP and UDP usage.
389          * @return Number of packets.
390          */
getTxPackets()391         public long getTxPackets() {
392             return mTxPackets;
393         }
394     }
395 
396     /**
397      * Fills the recycled bucket with data of the next bin in the enumeration.
398      * @param bucketOut Bucket to be filled with data.
399      * @return true if successfully filled the bucket, false otherwise.
400      */
getNextBucket(Bucket bucketOut)401     public boolean getNextBucket(Bucket bucketOut) {
402         if (mSummary != null) {
403             return getNextSummaryBucket(bucketOut);
404         } else {
405             return getNextHistoryBucket(bucketOut);
406         }
407     }
408 
409     /**
410      * Check if it is possible to ask for a next bucket in the enumeration.
411      * @return true if there is at least one more bucket.
412      */
hasNextBucket()413     public boolean hasNextBucket() {
414         if (mSummary != null) {
415             return mEnumerationIndex < mSummary.size();
416         } else if (mHistory != null) {
417             return mEnumerationIndex < mHistory.size()
418                     || hasNextUid();
419         }
420         return false;
421     }
422 
423     /**
424      * Closes the enumeration. Call this method before this object gets out of scope.
425      */
426     @Override
close()427     public void close() {
428         if (mSession != null) {
429             try {
430                 mSession.close();
431             } catch (RemoteException e) {
432                 Log.w(TAG, e);
433                 // Otherwise, meh
434             }
435         }
436         mSession = null;
437         if (mCloseGuard != null) {
438             mCloseGuard.close();
439         }
440     }
441 
442     // -------------------------END OF PUBLIC API-----------------------------------
443 
444     /**
445      * Collects device summary results into a Bucket.
446      * @throws RemoteException
447      */
getDeviceSummaryForNetwork()448     Bucket getDeviceSummaryForNetwork() throws RemoteException {
449         mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp);
450 
451         // Setting enumeration index beyond end to avoid accidental enumeration over data that does
452         // not belong to the calling user.
453         mEnumerationIndex = mSummary.size();
454 
455         return getSummaryAggregate();
456     }
457 
458     /**
459      * Collects summary results and sets summary enumeration mode.
460      * @throws RemoteException
461      */
startSummaryEnumeration()462     void startSummaryEnumeration() throws RemoteException {
463         mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp,
464                 false /* includeTags */);
465         mEnumerationIndex = 0;
466     }
467 
468     /**
469      * Collects history results for uid and resets history enumeration index.
470      */
startHistoryEnumeration(int uid)471     void startHistoryEnumeration(int uid) {
472         startHistoryEnumeration(uid, android.net.NetworkStats.TAG_NONE);
473     }
474 
475     /**
476      * Collects history results for uid and resets history enumeration index.
477      */
startHistoryEnumeration(int uid, int tag)478     void startHistoryEnumeration(int uid, int tag) {
479         mHistory = null;
480         try {
481             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
482                     android.net.NetworkStats.SET_ALL, tag,
483                     NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
484             setSingleUidTag(uid, tag);
485         } catch (RemoteException e) {
486             Log.w(TAG, e);
487             // Leaving mHistory null
488         }
489         mEnumerationIndex = 0;
490     }
491 
492     /**
493      * Starts uid enumeration for current user.
494      * @throws RemoteException
495      */
startUserUidEnumeration()496     void startUserUidEnumeration() throws RemoteException {
497         // TODO: getRelevantUids should be sensitive to time interval. When that's done,
498         //       the filtering logic below can be removed.
499         int[] uids = mSession.getRelevantUids();
500         // Filtering of uids with empty history.
501         IntArray filteredUids = new IntArray(uids.length);
502         for (int uid : uids) {
503             try {
504                 NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
505                         android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
506                         NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
507                 if (history != null && history.size() > 0) {
508                     filteredUids.add(uid);
509                 }
510             } catch (RemoteException e) {
511                 Log.w(TAG, "Error while getting history of uid " + uid, e);
512             }
513         }
514         mUids = filteredUids.toArray();
515         mUidOrUidIndex = -1;
516         stepHistory();
517     }
518 
519     /**
520      * Steps to next uid in enumeration and collects history for that.
521      */
stepHistory()522     private void stepHistory(){
523         if (hasNextUid()) {
524             stepUid();
525             mHistory = null;
526             try {
527                 mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(),
528                         android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
529                         NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
530             } catch (RemoteException e) {
531                 Log.w(TAG, e);
532                 // Leaving mHistory null
533             }
534             mEnumerationIndex = 0;
535         }
536     }
537 
fillBucketFromSummaryEntry(Bucket bucketOut)538     private void fillBucketFromSummaryEntry(Bucket bucketOut) {
539         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
540         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
541         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
542         bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
543         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
544         bucketOut.mBeginTimeStamp = mStartTimeStamp;
545         bucketOut.mEndTimeStamp = mEndTimeStamp;
546         bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
547         bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
548         bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
549         bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
550     }
551 
552     /**
553      * Getting the next item in summary enumeration.
554      * @param bucketOut Next item will be set here.
555      * @return true if a next item could be set.
556      */
getNextSummaryBucket(Bucket bucketOut)557     private boolean getNextSummaryBucket(Bucket bucketOut) {
558         if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
559             mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
560             fillBucketFromSummaryEntry(bucketOut);
561             return true;
562         }
563         return false;
564     }
565 
getSummaryAggregate()566     Bucket getSummaryAggregate() {
567         if (mSummary == null) {
568             return null;
569         }
570         Bucket bucket = new Bucket();
571         if (mRecycledSummaryEntry == null) {
572             mRecycledSummaryEntry = new android.net.NetworkStats.Entry();
573         }
574         mSummary.getTotal(mRecycledSummaryEntry);
575         fillBucketFromSummaryEntry(bucket);
576         return bucket;
577     }
578     /**
579      * Getting the next item in a history enumeration.
580      * @param bucketOut Next item will be set here.
581      * @return true if a next item could be set.
582      */
getNextHistoryBucket(Bucket bucketOut)583     private boolean getNextHistoryBucket(Bucket bucketOut) {
584         if (bucketOut != null && mHistory != null) {
585             if (mEnumerationIndex < mHistory.size()) {
586                 mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
587                         mRecycledHistoryEntry);
588                 bucketOut.mUid = Bucket.convertUid(getUid());
589                 bucketOut.mTag = Bucket.convertTag(mTag);
590                 bucketOut.mState = Bucket.STATE_ALL;
591                 bucketOut.mMetered = Bucket.METERED_ALL;
592                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
593                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
594                 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
595                         mRecycledHistoryEntry.bucketDuration;
596                 bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
597                 bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
598                 bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
599                 bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
600                 return true;
601             } else if (hasNextUid()) {
602                 stepHistory();
603                 return getNextHistoryBucket(bucketOut);
604             }
605         }
606         return false;
607     }
608 
609     // ------------------ UID LOGIC------------------------
610 
isUidEnumeration()611     private boolean isUidEnumeration() {
612         return mUids != null;
613     }
614 
hasNextUid()615     private boolean hasNextUid() {
616         return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
617     }
618 
getUid()619     private int getUid() {
620         // Check if uid enumeration.
621         if (isUidEnumeration()) {
622             if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
623                 throw new IndexOutOfBoundsException(
624                         "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
625             }
626             return mUids[mUidOrUidIndex];
627         }
628         // Single uid mode.
629         return mUidOrUidIndex;
630     }
631 
setSingleUidTag(int uid, int tag)632     private void setSingleUidTag(int uid, int tag) {
633         mUidOrUidIndex = uid;
634         mTag = tag;
635     }
636 
stepUid()637     private void stepUid() {
638         if (mUids != null) {
639             ++mUidOrUidIndex;
640         }
641     }
642 }
643