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