NetworkStatsManager.java revision 904237f83940676433104952d165d3b28d961044
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 an error happened during statistics collection. 244 * @throws SecurityException if permissions are insufficient to read network statistics. 245 */ 246 public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId, 247 long startTime, long endTime, int uid, int tag) throws SecurityException { 248 NetworkTemplate template; 249 template = createTemplate(networkType, subscriberId); 250 251 NetworkStats result; 252 try { 253 result = new NetworkStats(mContext, template, startTime, endTime); 254 result.startHistoryEnumeration(uid, tag); 255 } catch (RemoteException e) { 256 Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e); 257 return null; 258 } 259 260 return result; 261 } 262 263 /** 264 * Query network usage statistics details. Result filtered to include only uids belonging to 265 * calling user. Result is aggregated over state but not aggregated over time or uid. This means 266 * buckets' start and end timestamps are going to be between 'startTime' and 'endTime' 267 * parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid will vary, 268 * tag {@link NetworkStats.Bucket#TAG_NONE} and roaming is going to be 269 * {@link NetworkStats.Bucket#ROAMING_ALL}. 270 * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't 271 * interpolate across partial buckets. Since bucket length is in the order of hours, this 272 * method cannot be used to measure data usage on a fine grained time scale. 273 * 274 * @param networkType As defined in {@link ConnectivityManager}, e.g. 275 * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} 276 * etc. 277 * @param subscriberId If applicable, the subscriber id of the network interface. 278 * @param startTime Start of period. Defined in terms of "Unix time", see 279 * {@link java.lang.System#currentTimeMillis}. 280 * @param endTime End of period. Defined in terms of "Unix time", see 281 * {@link java.lang.System#currentTimeMillis}. 282 * @return Statistics object or null if permissions are insufficient or error happened during 283 * statistics collection. 284 */ 285 public NetworkStats queryDetails(int networkType, String subscriberId, long startTime, 286 long endTime) throws SecurityException, RemoteException { 287 NetworkTemplate template; 288 try { 289 template = createTemplate(networkType, subscriberId); 290 } catch (IllegalArgumentException e) { 291 if (DBG) Log.e(TAG, "Cannot create template", e); 292 return null; 293 } 294 295 NetworkStats result; 296 result = new NetworkStats(mContext, template, startTime, endTime); 297 result.startUserUidEnumeration(); 298 return result; 299 } 300 301 /** 302 * Registers to receive notifications about data usage on specified networks. 303 * 304 * #see registerUsageCallback(int, String[], long, UsageCallback, Handler) 305 */ 306 public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, 307 UsageCallback callback) { 308 registerUsageCallback(networkType, subscriberId, thresholdBytes, null /* handler */); 309 } 310 311 /** 312 * Registers to receive notifications about data usage on specified networks. 313 * 314 * <p>The callbacks will continue to be called as long as the process is live or 315 * {@link #unregisterUsageCallback} is called. 316 * 317 * @param networkType Type of network to monitor. Either 318 {@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}. 319 * @param subscriberId If applicable, the subscriber id of the network interface. 320 * @param thresholdBytes Threshold in bytes to be notified on. 321 * @param callback The {@link UsageCallback} that the system will call when data usage 322 * has exceeded the specified threshold. 323 * @param handler to dispatch callback events through, otherwise if {@code null} it uses 324 * the calling thread. 325 */ 326 public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, 327 UsageCallback callback, @Nullable Handler handler) { 328 checkNotNull(callback, "UsageCallback cannot be null"); 329 330 final Looper looper; 331 if (handler == null) { 332 looper = Looper.myLooper(); 333 } else { 334 looper = handler.getLooper(); 335 } 336 337 if (DBG) { 338 Log.d(TAG, "registerUsageCallback called with: {" 339 + " networkType=" + networkType 340 + " subscriberId=" + subscriberId 341 + " thresholdBytes=" + thresholdBytes 342 + " }"); 343 } 344 345 NetworkTemplate template = createTemplate(networkType, subscriberId); 346 DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET, 347 template, thresholdBytes); 348 try { 349 CallbackHandler callbackHandler = new CallbackHandler(looper, networkType, 350 subscriberId, callback); 351 callback.request = mService.registerUsageCallback( 352 mContext.getOpPackageName(), request, new Messenger(callbackHandler), 353 new Binder()); 354 if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request); 355 356 if (callback.request == null) { 357 Log.e(TAG, "Request from callback is null; should not happen"); 358 } 359 } catch (RemoteException e) { 360 if (DBG) Log.d(TAG, "Remote exception when registering callback"); 361 throw e.rethrowFromSystemServer(); 362 } 363 } 364 365 /** 366 * Unregisters callbacks on data usage. 367 * 368 * @param callback The {@link UsageCallback} used when registering. 369 */ 370 public void unregisterUsageCallback(UsageCallback callback) { 371 if (callback == null || callback.request == null 372 || callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) { 373 throw new IllegalArgumentException("Invalid UsageCallback"); 374 } 375 try { 376 mService.unregisterUsageRequest(callback.request); 377 } catch (RemoteException e) { 378 if (DBG) Log.d(TAG, "Remote exception when unregistering callback"); 379 throw e.rethrowFromSystemServer(); 380 } 381 } 382 383 /** 384 * Base class for usage callbacks. Should be extended by applications wanting notifications. 385 */ 386 public static abstract class UsageCallback { 387 388 /** 389 * Called when data usage has reached the given threshold. 390 */ 391 public abstract void onThresholdReached(int networkType, String subscriberId); 392 393 /** 394 * @hide used for internal bookkeeping 395 */ 396 private DataUsageRequest request; 397 } 398 399 private static NetworkTemplate createTemplate(int networkType, String subscriberId) { 400 NetworkTemplate template = null; 401 switch (networkType) { 402 case ConnectivityManager.TYPE_MOBILE: { 403 template = NetworkTemplate.buildTemplateMobileAll(subscriberId); 404 } break; 405 case ConnectivityManager.TYPE_WIFI: { 406 template = NetworkTemplate.buildTemplateWifiWildcard(); 407 } break; 408 default: { 409 throw new IllegalArgumentException("Cannot create template for network type " 410 + networkType + ", subscriberId '" 411 + NetworkIdentity.scrubSubscriberId(subscriberId) + "'."); 412 } 413 } 414 return template; 415 } 416 417 private static class CallbackHandler extends Handler { 418 private final int mNetworkType; 419 private final String mSubscriberId; 420 private UsageCallback mCallback; 421 422 CallbackHandler(Looper looper, int networkType, String subscriberId, 423 UsageCallback callback) { 424 super(looper); 425 mNetworkType = networkType; 426 mSubscriberId = subscriberId; 427 mCallback = callback; 428 } 429 430 @Override 431 public void handleMessage(Message message) { 432 DataUsageRequest request = 433 (DataUsageRequest) getObject(message, DataUsageRequest.PARCELABLE_KEY); 434 435 switch (message.what) { 436 case CALLBACK_LIMIT_REACHED: { 437 if (mCallback != null) { 438 mCallback.onThresholdReached(mNetworkType, mSubscriberId); 439 } else { 440 Log.e(TAG, "limit reached with released callback for " + request); 441 } 442 break; 443 } 444 case CALLBACK_RELEASED: { 445 if (DBG) Log.d(TAG, "callback released for " + request); 446 mCallback = null; 447 break; 448 } 449 } 450 } 451 452 private static Object getObject(Message msg, String key) { 453 return msg.getData().getParcelable(key); 454 } 455 } 456} 457