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 */ 100 NetworkStats(Context context, NetworkTemplate template, int flags, 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(flags, 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({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 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 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 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 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 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 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 */ 289 public int getUid() { 290 return mUid; 291 } 292 293 /** 294 * Tag of the bucket.<p /> 295 * @return Bucket tag. 296 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 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 */ 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 611 private boolean isUidEnumeration() { 612 return mUids != null; 613 } 614 615 private boolean hasNextUid() { 616 return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length; 617 } 618 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 632 private void setSingleUidTag(int uid, int tag) { 633 mUidOrUidIndex = uid; 634 mTag = tag; 635 } 636 637 private void stepUid() { 638 if (mUids != null) { 639 ++mUidOrUidIndex; 640 } 641 } 642 } 643