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