NetworkStatsManager.java revision d06a9004edefd250f7aa614efb714a40447ff7f5
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 17package android.app.usage; 18 19import static com.android.internal.util.Preconditions.checkNotNull; 20 21import android.annotation.Nullable; 22import android.annotation.SystemService; 23import android.app.usage.NetworkStats.Bucket; 24import android.content.Context; 25import android.net.ConnectivityManager; 26import android.net.DataUsageRequest; 27import android.net.INetworkStatsService; 28import android.net.NetworkIdentity; 29import android.net.NetworkTemplate; 30import android.os.Binder; 31import android.os.Handler; 32import android.os.Looper; 33import android.os.Message; 34import android.os.Messenger; 35import android.os.RemoteException; 36import android.os.ServiceManager; 37import android.os.ServiceManager.ServiceNotFoundException; 38import android.util.DataUnit; 39import android.util.Log; 40 41import com.android.internal.annotations.VisibleForTesting; 42 43/** 44 * Provides access to network usage history and statistics. Usage data is collected in 45 * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details. 46 * <p /> 47 * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and 48 * Long.MAX_VALUE can be used to simulate open ended intervals). By default, apps can only obtain 49 * data about themselves. See the below note for special cases in which apps can obtain data about 50 * other applications. 51 * <h3> 52 * Summary queries 53 * </h3> 54 * {@link #querySummaryForDevice} <p /> 55 * {@link #querySummaryForUser} <p /> 56 * {@link #querySummary} <p /> 57 * These queries aggregate network usage across the whole interval. Therefore there will be only one 58 * bucket for a particular key, state, metered and roaming combination. In case of the user-wide 59 * and device-wide summaries a single bucket containing the totalised network usage is returned. 60 * <h3> 61 * History queries 62 * </h3> 63 * {@link #queryDetailsForUid} <p /> 64 * {@link #queryDetails} <p /> 65 * These queries do not aggregate over time but do aggregate over state, metered and roaming. 66 * Therefore there can be multiple buckets for a particular key. However, all Buckets will have 67 * {@code state} {@link NetworkStats.Bucket#STATE_ALL}, 68 * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, 69 * {@code metered } {@link NetworkStats.Bucket#METERED_ALL}, 70 * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}. 71 * <p /> 72 * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the 73 * calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, 74 * which is a system-level permission and will not be granted to third-party apps. However, 75 * declaring the permission implies intention to use the API and the user of the device can grant 76 * permission through the Settings application. 77 * <p /> 78 * Profile owner apps are automatically granted permission to query data on the profile they manage 79 * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier- 80 * privileged apps likewise get access to usage data for all users on the device. 81 * <p /> 82 * In addition to tethering usage, usage by removed users and apps, and usage by the system 83 * is also included in the results for callers with one of these higher levels of access. 84 * <p /> 85 * <b>NOTE:</b> Prior to API level {@value android.os.Build.VERSION_CODES#N}, all calls to these APIs required 86 * the above permission, even to access an app's own data usage, and carrier-privileged apps were 87 * not included. 88 */ 89@SystemService(Context.NETWORK_STATS_SERVICE) 90public class NetworkStatsManager { 91 private static final String TAG = "NetworkStatsManager"; 92 private static final boolean DBG = false; 93 94 /** @hide */ 95 public static final int CALLBACK_LIMIT_REACHED = 0; 96 /** @hide */ 97 public static final int CALLBACK_RELEASED = 1; 98 99 /** 100 * Minimum data usage threshold for registering usage callbacks. 101 * 102 * Requests registered with a threshold lower than this will only be triggered once this minimum 103 * is reached. 104 * @hide 105 */ 106 public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2); 107 108 private final Context mContext; 109 private final INetworkStatsService mService; 110 111 /** @hide */ 112 public static final int FLAG_POLL_ON_OPEN = 1 << 0; 113 /** @hide */ 114 public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 1; 115 116 private int mFlags; 117 118 /** 119 * {@hide} 120 */ 121 public NetworkStatsManager(Context context) throws ServiceNotFoundException { 122 this(context, INetworkStatsService.Stub.asInterface( 123 ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE))); 124 } 125 126 /** @hide */ 127 @VisibleForTesting 128 public NetworkStatsManager(Context context, INetworkStatsService service) { 129 mContext = context; 130 mService = service; 131 setPollOnOpen(true); 132 } 133 134 /** @hide */ 135 public void setPollOnOpen(boolean pollOnOpen) { 136 if (pollOnOpen) { 137 mFlags |= FLAG_POLL_ON_OPEN; 138 } else { 139 mFlags &= ~FLAG_POLL_ON_OPEN; 140 } 141 } 142 143 /** @hide */ 144 public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) { 145 if (augmentWithSubscriptionPlan) { 146 mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN; 147 } else { 148 mFlags &= ~FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN; 149 } 150 } 151 152 /** @hide */ 153 public Bucket querySummaryForDevice(NetworkTemplate template, 154 long startTime, long endTime) throws SecurityException, RemoteException { 155 Bucket bucket = null; 156 NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, 157 mService); 158 bucket = stats.getDeviceSummaryForNetwork(); 159 160 stats.close(); 161 return bucket; 162 } 163 164 /** 165 * Query network usage statistics summaries. Result is summarised data usage for the whole 166 * device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and 167 * roaming. This means the bucket's start and end timestamp are going to be the same as the 168 * 'startTime' and 'endTime' parameters. State is going to be 169 * {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL}, 170 * tag {@link NetworkStats.Bucket#TAG_NONE}, 171 * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, 172 * metered {@link NetworkStats.Bucket#METERED_ALL}, 173 * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}. 174 * 175 * @param networkType As defined in {@link ConnectivityManager}, e.g. 176 * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} 177 * etc. 178 * @param subscriberId If applicable, the subscriber id of the network interface. 179 * @param startTime Start of period. Defined in terms of "Unix time", see 180 * {@link java.lang.System#currentTimeMillis}. 181 * @param endTime End of period. Defined in terms of "Unix time", see 182 * {@link java.lang.System#currentTimeMillis}. 183 * @return Bucket object or null if permissions are insufficient or error happened during 184 * statistics collection. 185 */ 186 public Bucket querySummaryForDevice(int networkType, String subscriberId, 187 long startTime, long endTime) throws SecurityException, RemoteException { 188 NetworkTemplate template; 189 try { 190 template = createTemplate(networkType, subscriberId); 191 } catch (IllegalArgumentException e) { 192 if (DBG) Log.e(TAG, "Cannot create template", e); 193 return null; 194 } 195 196 return querySummaryForDevice(template, startTime, endTime); 197 } 198 199 /** 200 * Query network usage statistics summaries. Result is summarised data usage for all uids 201 * belonging to calling user. Result is a single Bucket aggregated over time, state and uid. 202 * This means the bucket's start and end timestamp are going to be the same as the 'startTime' 203 * and 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, 204 * uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE}, 205 * metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming 206 * {@link NetworkStats.Bucket#ROAMING_ALL}. 207 * 208 * @param networkType As defined in {@link ConnectivityManager}, e.g. 209 * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} 210 * etc. 211 * @param subscriberId If applicable, the subscriber id of the network interface. 212 * @param startTime Start of period. Defined in terms of "Unix time", see 213 * {@link java.lang.System#currentTimeMillis}. 214 * @param endTime End of period. Defined in terms of "Unix time", see 215 * {@link java.lang.System#currentTimeMillis}. 216 * @return Bucket object or null if permissions are insufficient or error happened during 217 * statistics collection. 218 */ 219 public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime, 220 long endTime) throws SecurityException, RemoteException { 221 NetworkTemplate template; 222 try { 223 template = createTemplate(networkType, subscriberId); 224 } catch (IllegalArgumentException e) { 225 if (DBG) Log.e(TAG, "Cannot create template", e); 226 return null; 227 } 228 229 NetworkStats stats; 230 stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); 231 stats.startSummaryEnumeration(); 232 233 stats.close(); 234 return stats.getSummaryAggregate(); 235 } 236 237 /** 238 * Query network usage statistics summaries. Result filtered to include only uids belonging to 239 * calling user. Result is aggregated over time, hence all buckets will have the same start and 240 * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This 241 * means buckets' start and end timestamps are going to be the same as the 'startTime' and 242 * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to 243 * be the same. 244 * 245 * @param networkType As defined in {@link ConnectivityManager}, e.g. 246 * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} 247 * etc. 248 * @param subscriberId If applicable, the subscriber id of the network interface. 249 * @param startTime Start of period. Defined in terms of "Unix time", see 250 * {@link java.lang.System#currentTimeMillis}. 251 * @param endTime End of period. Defined in terms of "Unix time", see 252 * {@link java.lang.System#currentTimeMillis}. 253 * @return Statistics object or null if permissions are insufficient or error happened during 254 * statistics collection. 255 */ 256 public NetworkStats querySummary(int networkType, String subscriberId, long startTime, 257 long endTime) throws SecurityException, RemoteException { 258 NetworkTemplate template; 259 try { 260 template = createTemplate(networkType, subscriberId); 261 } catch (IllegalArgumentException e) { 262 if (DBG) Log.e(TAG, "Cannot create template", e); 263 return null; 264 } 265 266 NetworkStats result; 267 result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); 268 result.startSummaryEnumeration(); 269 270 return result; 271 } 272 273 /** 274 * Query network usage statistics details for a given uid. 275 * 276 * #see queryDetailsForUidTagState(int, String, long, long, int, int, int) 277 */ 278 public NetworkStats queryDetailsForUid(int networkType, String subscriberId, 279 long startTime, long endTime, int uid) throws SecurityException { 280 return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid, 281 NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL); 282 } 283 284 /** 285 * Query network usage statistics details for a given uid and tag. 286 * 287 * #see queryDetailsForUidTagState(int, String, long, long, int, int, int) 288 */ 289 public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId, 290 long startTime, long endTime, int uid, int tag) throws SecurityException { 291 return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid, 292 tag, NetworkStats.Bucket.STATE_ALL); 293 } 294 295 /** 296 * Query network usage statistics details for a given uid, tag, and state. Only usable for uids 297 * belonging to calling user. Result is not aggregated over time. This means buckets' start and 298 * end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going 299 * to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state 300 * the same as the 'state' parameter. 301 * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, 302 * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and 303 * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. 304 * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't 305 * interpolate across partial buckets. Since bucket length is in the order of hours, this 306 * method cannot be used to measure data usage on a fine grained time scale. 307 * 308 * @param networkType As defined in {@link ConnectivityManager}, e.g. 309 * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} 310 * etc. 311 * @param subscriberId If applicable, the subscriber id of the network interface. 312 * @param startTime Start of period. Defined in terms of "Unix time", see 313 * {@link java.lang.System#currentTimeMillis}. 314 * @param endTime End of period. Defined in terms of "Unix time", see 315 * {@link java.lang.System#currentTimeMillis}. 316 * @param uid UID of app 317 * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags. 318 * @return Statistics object or null if an error happened during statistics collection. 319 * @throws SecurityException if permissions are insufficient to read network statistics. 320 */ 321 public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId, 322 long startTime, long endTime, int uid, int tag, int state) throws SecurityException { 323 NetworkTemplate template; 324 template = createTemplate(networkType, subscriberId); 325 326 NetworkStats result; 327 try { 328 result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); 329 result.startHistoryEnumeration(uid, tag, state); 330 } catch (RemoteException e) { 331 Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag 332 + " state=" + state, e); 333 return null; 334 } 335 336 return result; 337 } 338 339 /** 340 * Query network usage statistics details. Result filtered to include only uids belonging to 341 * calling user. Result is aggregated over state but not aggregated over time, uid, tag, 342 * metered, nor roaming. This means buckets' start and end timestamps are going to be between 343 * 'startTime' and 'endTime' parameters. State is going to be 344 * {@link NetworkStats.Bucket#STATE_ALL}, uid will vary, 345 * tag {@link NetworkStats.Bucket#TAG_NONE}, 346 * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, 347 * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, 348 * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. 349 * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't 350 * interpolate across partial buckets. Since bucket length is in the order of hours, this 351 * method cannot be used to measure data usage on a fine grained time scale. 352 * 353 * @param networkType As defined in {@link ConnectivityManager}, e.g. 354 * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} 355 * etc. 356 * @param subscriberId If applicable, the subscriber id of the network interface. 357 * @param startTime Start of period. Defined in terms of "Unix time", see 358 * {@link java.lang.System#currentTimeMillis}. 359 * @param endTime End of period. Defined in terms of "Unix time", see 360 * {@link java.lang.System#currentTimeMillis}. 361 * @return Statistics object or null if permissions are insufficient or error happened during 362 * statistics collection. 363 */ 364 public NetworkStats queryDetails(int networkType, String subscriberId, long startTime, 365 long endTime) throws SecurityException, RemoteException { 366 NetworkTemplate template; 367 try { 368 template = createTemplate(networkType, subscriberId); 369 } catch (IllegalArgumentException e) { 370 if (DBG) Log.e(TAG, "Cannot create template", e); 371 return null; 372 } 373 374 NetworkStats result; 375 result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); 376 result.startUserUidEnumeration(); 377 return result; 378 } 379 380 /** @hide */ 381 public void registerUsageCallback(NetworkTemplate template, int networkType, 382 long thresholdBytes, UsageCallback callback, @Nullable Handler handler) { 383 checkNotNull(callback, "UsageCallback cannot be null"); 384 385 final Looper looper; 386 if (handler == null) { 387 looper = Looper.myLooper(); 388 } else { 389 looper = handler.getLooper(); 390 } 391 392 DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET, 393 template, thresholdBytes); 394 try { 395 CallbackHandler callbackHandler = new CallbackHandler(looper, networkType, 396 template.getSubscriberId(), callback); 397 callback.request = mService.registerUsageCallback( 398 mContext.getOpPackageName(), request, new Messenger(callbackHandler), 399 new Binder()); 400 if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request); 401 402 if (callback.request == null) { 403 Log.e(TAG, "Request from callback is null; should not happen"); 404 } 405 } catch (RemoteException e) { 406 if (DBG) Log.d(TAG, "Remote exception when registering callback"); 407 throw e.rethrowFromSystemServer(); 408 } 409 } 410 411 /** 412 * Registers to receive notifications about data usage on specified networks. 413 * 414 * #see registerUsageCallback(int, String[], long, UsageCallback, Handler) 415 */ 416 public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, 417 UsageCallback callback) { 418 registerUsageCallback(networkType, subscriberId, thresholdBytes, callback, 419 null /* handler */); 420 } 421 422 /** 423 * Registers to receive notifications about data usage on specified networks. 424 * 425 * <p>The callbacks will continue to be called as long as the process is live or 426 * {@link #unregisterUsageCallback} is called. 427 * 428 * @param networkType Type of network to monitor. Either 429 {@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}. 430 * @param subscriberId If applicable, the subscriber id of the network interface. 431 * @param thresholdBytes Threshold in bytes to be notified on. 432 * @param callback The {@link UsageCallback} that the system will call when data usage 433 * has exceeded the specified threshold. 434 * @param handler to dispatch callback events through, otherwise if {@code null} it uses 435 * the calling thread. 436 */ 437 public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, 438 UsageCallback callback, @Nullable Handler handler) { 439 NetworkTemplate template = createTemplate(networkType, subscriberId); 440 if (DBG) { 441 Log.d(TAG, "registerUsageCallback called with: {" 442 + " networkType=" + networkType 443 + " subscriberId=" + subscriberId 444 + " thresholdBytes=" + thresholdBytes 445 + " }"); 446 } 447 registerUsageCallback(template, networkType, thresholdBytes, callback, handler); 448 } 449 450 /** 451 * Unregisters callbacks on data usage. 452 * 453 * @param callback The {@link UsageCallback} used when registering. 454 */ 455 public void unregisterUsageCallback(UsageCallback callback) { 456 if (callback == null || callback.request == null 457 || callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) { 458 throw new IllegalArgumentException("Invalid UsageCallback"); 459 } 460 try { 461 mService.unregisterUsageRequest(callback.request); 462 } catch (RemoteException e) { 463 if (DBG) Log.d(TAG, "Remote exception when unregistering callback"); 464 throw e.rethrowFromSystemServer(); 465 } 466 } 467 468 /** 469 * Base class for usage callbacks. Should be extended by applications wanting notifications. 470 */ 471 public static abstract class UsageCallback { 472 473 /** 474 * Called when data usage has reached the given threshold. 475 */ 476 public abstract void onThresholdReached(int networkType, String subscriberId); 477 478 /** 479 * @hide used for internal bookkeeping 480 */ 481 private DataUsageRequest request; 482 } 483 484 private static NetworkTemplate createTemplate(int networkType, String subscriberId) { 485 final NetworkTemplate template; 486 switch (networkType) { 487 case ConnectivityManager.TYPE_MOBILE: 488 template = subscriberId == null 489 ? NetworkTemplate.buildTemplateMobileWildcard() 490 : NetworkTemplate.buildTemplateMobileAll(subscriberId); 491 break; 492 case ConnectivityManager.TYPE_WIFI: 493 template = NetworkTemplate.buildTemplateWifiWildcard(); 494 break; 495 default: 496 throw new IllegalArgumentException("Cannot create template for network type " 497 + networkType + ", subscriberId '" 498 + NetworkIdentity.scrubSubscriberId(subscriberId) + "'."); 499 } 500 return template; 501 } 502 503 private static class CallbackHandler extends Handler { 504 private final int mNetworkType; 505 private final String mSubscriberId; 506 private UsageCallback mCallback; 507 508 CallbackHandler(Looper looper, int networkType, String subscriberId, 509 UsageCallback callback) { 510 super(looper); 511 mNetworkType = networkType; 512 mSubscriberId = subscriberId; 513 mCallback = callback; 514 } 515 516 @Override 517 public void handleMessage(Message message) { 518 DataUsageRequest request = 519 (DataUsageRequest) getObject(message, DataUsageRequest.PARCELABLE_KEY); 520 521 switch (message.what) { 522 case CALLBACK_LIMIT_REACHED: { 523 if (mCallback != null) { 524 mCallback.onThresholdReached(mNetworkType, mSubscriberId); 525 } else { 526 Log.e(TAG, "limit reached with released callback for " + request); 527 } 528 break; 529 } 530 case CALLBACK_RELEASED: { 531 if (DBG) Log.d(TAG, "callback released for " + request); 532 mCallback = null; 533 break; 534 } 535 } 536 } 537 538 private static Object getObject(Message msg, String key) { 539 return msg.getData().getParcelable(key); 540 } 541 } 542} 543