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, 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