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