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