blob: 5f97c9e1d4fd28feb0e71cfecb720ea2488ee9e9 [file] [log] [blame]
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +00001/**
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.content.Context;
20import android.net.INetworkStatsService;
21import android.net.INetworkStatsSession;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000022import android.net.NetworkStatsHistory;
23import android.net.NetworkTemplate;
24import android.net.TrafficStats;
25import android.os.RemoteException;
26import android.os.ServiceManager;
Zoltan Szatmary-Ban887cffe2015-08-13 16:53:10 +010027import android.util.IntArray;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000028import android.util.Log;
29
30import dalvik.system.CloseGuard;
31
32/**
Zoltan Szatmary-Bandf70dce2015-06-04 14:07:55 +010033 * Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000034 * are returned as results to various queries in {@link NetworkStatsManager}.
35 */
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +010036public final class NetworkStats implements AutoCloseable {
Zoltan Szatmary-Bandf70dce2015-06-04 14:07:55 +010037 private final static String TAG = "NetworkStats";
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000038
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 */
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +010073 private android.net.NetworkStats mSummary = null;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000074
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 */
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +010088 private android.net.NetworkStats.Entry mRecycledSummaryEntry = null;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000089 private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
90
91 /** @hide */
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +010092 NetworkStats(Context context, NetworkTemplate template, long startTimestamp,
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +000093 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
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 /**
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800124 * Combined usage across all states.
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000125 */
126 public static final int STATE_ALL = -1;
127
128 /**
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800129 * Usage not accounted for in any other state.
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000130 */
131 public static final int STATE_DEFAULT = 0x1;
132
133 /**
134 * Foreground usage.
135 */
136 public static final int STATE_FOREGROUND = 0x2;
137
138 /**
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +0100139 * Special UID value for aggregate/unspecified.
140 */
141 public static final int UID_ALL = android.net.NetworkStats.UID_ALL;
142
143 /**
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000144 * Special UID value for removed apps.
145 */
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +0100146 public static final int UID_REMOVED = TrafficStats.UID_REMOVED;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000147
148 /**
149 * Special UID value for data usage by tethering.
150 */
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +0100151 public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000152
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800153 /**
154 * Combined usage across all metering states.
155 */
156 public static final int METERING_ALL = -1;
157
158 /**
159 * Usage not accounted for in any other metering state.
160 */
161 public static final int METERING_DEFAULT = 0x1;
162
163 /**
164 * Metered usage.
165 */
166 public static final int METERING_METERED = 0x2;
167
168 /**
169 * Combined usage across all roaming states.
170 */
171 public static final int ROAMING_ALL = -1;
172
173 /**
174 * Usage not accounted for in any other roaming state.
175 */
176 public static final int ROAMING_DEFAULT = 0x1;
177
178 /**
179 * Roaming usage.
180 */
181 public static final int ROAMING_ROAMING = 0x2;
182
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000183 private int mUid;
184 private int mState;
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800185 private int mMetering;
186 private int mRoaming;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000187 private long mBeginTimeStamp;
188 private long mEndTimeStamp;
189 private long mRxBytes;
190 private long mRxPackets;
191 private long mTxBytes;
192 private long mTxPackets;
193
194 private static int convertState(int networkStatsSet) {
195 switch (networkStatsSet) {
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +0100196 case android.net.NetworkStats.SET_ALL : return STATE_ALL;
197 case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
198 case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000199 }
200 return 0;
201 }
202
203 private static int convertUid(int uid) {
204 switch (uid) {
205 case TrafficStats.UID_REMOVED: return UID_REMOVED;
206 case TrafficStats.UID_TETHERING: return UID_TETHERING;
207 }
208 return uid;
209 }
210
211 public Bucket() {
212 }
213
214 /**
215 * Key of the bucket. Usually an app uid or one of the following special values:<p />
216 * <ul>
217 * <li>{@link #UID_REMOVED}</li>
218 * <li>{@link #UID_TETHERING}</li>
219 * <li>{@link android.os.Process#SYSTEM_UID}</li>
220 * </ul>
221 * @return Bucket key.
222 */
223 public int getUid() {
224 return mUid;
225 }
226
227 /**
228 * Usage state. One of the following values:<p/>
229 * <ul>
230 * <li>{@link #STATE_ALL}</li>
231 * <li>{@link #STATE_DEFAULT}</li>
232 * <li>{@link #STATE_FOREGROUND}</li>
233 * </ul>
234 * @return Usage state.
235 */
236 public int getState() {
237 return mState;
238 }
239
240 /**
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800241 * Metering state. One of the following values:<p/>
242 * <ul>
243 * <li>{@link #METERING_ALL}</li>
244 * <li>{@link #METERING_DEFAULT}</li>
245 * <li>{@link #METERING_METERED}</li>
246 * </ul>
247 */
248 public int getMetering() {
249 return mMetering;
250 }
251
252 /**
253 * Roaming state. One of the following values:<p/>
254 * <ul>
255 * <li>{@link #ROAMING_ALL}</li>
256 * <li>{@link #ROAMING_DEFAULT}</li>
257 * <li>{@link #ROAMING_ROAMING}</li>
258 * </ul>
259 */
260 public int getRoaming() {
261 return mRoaming;
262 }
263
264 /**
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000265 * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
266 * {@link java.lang.System#currentTimeMillis}.
267 * @return Start of interval.
268 */
269 public long getStartTimeStamp() {
270 return mBeginTimeStamp;
271 }
272
273 /**
274 * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
275 * {@link java.lang.System#currentTimeMillis}.
276 * @return End of interval.
277 */
278 public long getEndTimeStamp() {
279 return mEndTimeStamp;
280 }
281
282 /**
283 * Number of bytes received during the bucket's time interval. Statistics are measured at
284 * the network layer, so they include both TCP and UDP usage.
285 * @return Number of bytes.
286 */
287 public long getRxBytes() {
288 return mRxBytes;
289 }
290
291 /**
292 * Number of bytes transmitted during the bucket's time interval. Statistics are measured at
293 * the network layer, so they include both TCP and UDP usage.
294 * @return Number of bytes.
295 */
296 public long getTxBytes() {
297 return mTxBytes;
298 }
299
300 /**
301 * Number of packets received during the bucket's time interval. Statistics are measured at
302 * the network layer, so they include both TCP and UDP usage.
303 * @return Number of packets.
304 */
305 public long getRxPackets() {
306 return mRxPackets;
307 }
308
309 /**
310 * Number of packets transmitted during the bucket's time interval. Statistics are measured
311 * at the network layer, so they include both TCP and UDP usage.
312 * @return Number of packets.
313 */
314 public long getTxPackets() {
315 return mTxPackets;
316 }
317 }
318
319 /**
320 * Fills the recycled bucket with data of the next bin in the enumeration.
321 * @param bucketOut Bucket to be filled with data.
322 * @return true if successfully filled the bucket, false otherwise.
323 */
324 public boolean getNextBucket(Bucket bucketOut) {
325 if (mSummary != null) {
326 return getNextSummaryBucket(bucketOut);
327 } else {
328 return getNextHistoryBucket(bucketOut);
329 }
330 }
331
332 /**
333 * Check if it is possible to ask for a next bucket in the enumeration.
334 * @return true if there is at least one more bucket.
335 */
336 public boolean hasNextBucket() {
337 if (mSummary != null) {
338 return mEnumerationIndex < mSummary.size();
339 } else if (mHistory != null) {
340 return mEnumerationIndex < mHistory.size()
341 || hasNextUid();
342 }
343 return false;
344 }
345
346 /**
347 * Closes the enumeration. Call this method before this object gets out of scope.
348 */
349 @Override
350 public void close() {
351 if (mSession != null) {
352 try {
353 mSession.close();
354 } catch (RemoteException e) {
355 Log.w(TAG, e);
356 // Otherwise, meh
357 }
358 }
359 mSession = null;
360 if (mCloseGuard != null) {
361 mCloseGuard.close();
362 }
363 }
364
365 // -------------------------END OF PUBLIC API-----------------------------------
366
367 /**
368 * Collects device summary results into a Bucket.
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000369 * @throws RemoteException
370 */
Zoltan Szatmary-Ban72027d22015-06-16 15:49:16 +0100371 Bucket getDeviceSummaryForNetwork() throws RemoteException {
372 mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp);
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000373
374 // Setting enumeration index beyond end to avoid accidental enumeration over data that does
375 // not belong to the calling user.
376 mEnumerationIndex = mSummary.size();
377
378 return getSummaryAggregate();
379 }
380
381 /**
382 * Collects summary results and sets summary enumeration mode.
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000383 * @throws RemoteException
384 */
Zoltan Szatmary-Ban72027d22015-06-16 15:49:16 +0100385 void startSummaryEnumeration() throws RemoteException {
386 mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp, false);
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000387
388 mEnumerationIndex = 0;
389 }
390
391 /**
392 * Collects history results for uid and resets history enumeration index.
393 */
394 void startHistoryEnumeration(int uid) {
395 mHistory = null;
396 try {
Zoltan Szatmary-Ban72027d22015-06-16 15:49:16 +0100397 mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
398 android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
399 NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000400 setSingleUid(uid);
401 } catch (RemoteException e) {
402 Log.w(TAG, e);
403 // Leaving mHistory null
404 }
405 mEnumerationIndex = 0;
406 }
407
408 /**
409 * Starts uid enumeration for current user.
410 * @throws RemoteException
411 */
412 void startUserUidEnumeration() throws RemoteException {
Zoltan Szatmary-Ban887cffe2015-08-13 16:53:10 +0100413 // TODO: getRelevantUids should be sensitive to time interval. When that's done,
414 // the filtering logic below can be removed.
415 int[] uids = mSession.getRelevantUids();
416 // Filtering of uids with empty history.
417 IntArray filteredUids = new IntArray(uids.length);
418 for (int uid : uids) {
419 try {
420 NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
421 android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
422 NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
423 if (history != null && history.size() > 0) {
424 filteredUids.add(uid);
425 }
426 } catch (RemoteException e) {
427 Log.w(TAG, "Error while getting history of uid " + uid, e);
428 }
429 }
430 mUids = filteredUids.toArray();
431 mUidOrUidIndex = -1;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000432 stepHistory();
433 }
434
435 /**
436 * Steps to next uid in enumeration and collects history for that.
437 */
438 private void stepHistory(){
439 if (hasNextUid()) {
440 stepUid();
441 mHistory = null;
442 try {
Zoltan Szatmary-Ban72027d22015-06-16 15:49:16 +0100443 mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(),
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +0100444 android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
Zoltan Szatmary-Ban72027d22015-06-16 15:49:16 +0100445 NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000446 } catch (RemoteException e) {
447 Log.w(TAG, e);
448 // Leaving mHistory null
449 }
450 mEnumerationIndex = 0;
451 }
452 }
453
454 private void fillBucketFromSummaryEntry(Bucket bucketOut) {
455 bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
456 bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800457 // TODO: Implement metering/roaming tracking.
458 bucketOut.mMetering = Bucket.METERING_ALL;
459 bucketOut.mRoaming = Bucket.ROAMING_ALL;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000460 bucketOut.mBeginTimeStamp = mStartTimeStamp;
461 bucketOut.mEndTimeStamp = mEndTimeStamp;
462 bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
463 bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
464 bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
465 bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
466 }
467
468 /**
469 * Getting the next item in summary enumeration.
470 * @param bucketOut Next item will be set here.
471 * @return true if a next item could be set.
472 */
473 private boolean getNextSummaryBucket(Bucket bucketOut) {
474 if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
475 mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
476 fillBucketFromSummaryEntry(bucketOut);
477 return true;
478 }
479 return false;
480 }
481
482 Bucket getSummaryAggregate() {
483 if (mSummary == null) {
484 return null;
485 }
486 Bucket bucket = new Bucket();
487 if (mRecycledSummaryEntry == null) {
Zoltan Szatmary-Ban381483b2015-05-13 17:53:17 +0100488 mRecycledSummaryEntry = new android.net.NetworkStats.Entry();
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000489 }
490 mSummary.getTotal(mRecycledSummaryEntry);
491 fillBucketFromSummaryEntry(bucket);
492 return bucket;
493 }
494 /**
495 * Getting the next item in a history enumeration.
496 * @param bucketOut Next item will be set here.
497 * @return true if a next item could be set.
498 */
499 private boolean getNextHistoryBucket(Bucket bucketOut) {
500 if (bucketOut != null && mHistory != null) {
501 if (mEnumerationIndex < mHistory.size()) {
502 mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
503 mRecycledHistoryEntry);
504 bucketOut.mUid = Bucket.convertUid(getUid());
505 bucketOut.mState = Bucket.STATE_ALL;
Jeff Davidson05ae0fe2016-01-06 13:53:36 -0800506 bucketOut.mMetering = Bucket.METERING_ALL;
507 bucketOut.mRoaming = Bucket.ROAMING_ALL;
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000508 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
509 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
510 mRecycledHistoryEntry.bucketDuration;
511 bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
512 bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
513 bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
514 bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
515 return true;
516 } else if (hasNextUid()) {
517 stepHistory();
518 return getNextHistoryBucket(bucketOut);
519 }
520 }
521 return false;
522 }
523
524 // ------------------ UID LOGIC------------------------
525
526 private boolean isUidEnumeration() {
527 return mUids != null;
528 }
529
530 private boolean hasNextUid() {
531 return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
532 }
533
534 private int getUid() {
535 // Check if uid enumeration.
536 if (isUidEnumeration()) {
537 if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
538 throw new IndexOutOfBoundsException(
539 "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
540 }
541 return mUids[mUidOrUidIndex];
542 }
543 // Single uid mode.
544 return mUidOrUidIndex;
545 }
546
547 private void setSingleUid(int uid) {
548 mUidOrUidIndex = uid;
549 }
550
Zoltan Szatmary-Ban9c5dfa52015-02-23 17:20:20 +0000551 private void stepUid() {
552 if (mUids != null) {
553 ++mUidOrUidIndex;
554 }
555 }
556}