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, callback, 309 null /* handler */); 310 } 311 312 /** 313 * Registers to receive notifications about data usage on specified networks. 314 * 315 * <p>The callbacks will continue to be called as long as the process is live or 316 * {@link #unregisterUsageCallback} is called. 317 * 318 * @param networkType Type of network to monitor. Either 319 {@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}. 320 * @param subscriberId If applicable, the subscriber id of the network interface. 321 * @param thresholdBytes Threshold in bytes to be notified on. 322 * @param callback The {@link UsageCallback} that the system will call when data usage 323 * has exceeded the specified threshold. 324 * @param handler to dispatch callback events through, otherwise if {@code null} it uses 325 * the calling thread. 326 */ 327 public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, 328 UsageCallback callback, @Nullable Handler handler) { 329 checkNotNull(callback, "UsageCallback cannot be null"); 330 331 final Looper looper; 332 if (handler == null) { 333 looper = Looper.myLooper(); 334 } else { 335 looper = handler.getLooper(); 336 } 337 338 if (DBG) { 339 Log.d(TAG, "registerUsageCallback called with: {" 340 + " networkType=" + networkType 341 + " subscriberId=" + subscriberId 342 + " thresholdBytes=" + thresholdBytes 343 + " }"); 344 } 345 346 NetworkTemplate template = createTemplate(networkType, subscriberId); 347 DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET, 348 template, thresholdBytes); 349 try { 350 CallbackHandler callbackHandler = new CallbackHandler(looper, networkType, 351 subscriberId, callback); 352 callback.request = mService.registerUsageCallback( 353 mContext.getOpPackageName(), request, new Messenger(callbackHandler), 354 new Binder()); 355 if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request); 356 357 if (callback.request == null) { 358 Log.e(TAG, "Request from callback is null; should not happen"); 359 } 360 } catch (RemoteException e) { 361 if (DBG) Log.d(TAG, "Remote exception when registering callback"); 362 throw e.rethrowFromSystemServer(); 363 } 364 } 365 366 /** 367 * Unregisters callbacks on data usage. 368 * 369 * @param callback The {@link UsageCallback} used when registering. 370 */ 371 public void unregisterUsageCallback(UsageCallback callback) { 372 if (callback == null || callback.request == null 373 || callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) { 374 throw new IllegalArgumentException("Invalid UsageCallback"); 375 } 376 try { 377 mService.unregisterUsageRequest(callback.request); 378 } catch (RemoteException e) { 379 if (DBG) Log.d(TAG, "Remote exception when unregistering callback"); 380 throw e.rethrowFromSystemServer(); 381 } 382 } 383 384 /** 385 * Base class for usage callbacks. Should be extended by applications wanting notifications. 386 */ 387 public static abstract class UsageCallback { 388 389 /** 390 * Called when data usage has reached the given threshold. 391 */ 392 public abstract void onThresholdReached(int networkType, String subscriberId); 393 394 /** 395 * @hide used for internal bookkeeping 396 */ 397 private DataUsageRequest request; 398 } 399 400 private static NetworkTemplate createTemplate(int networkType, String subscriberId) { 401 NetworkTemplate template = null; 402 switch (networkType) { 403 case ConnectivityManager.TYPE_MOBILE: { 404 template = NetworkTemplate.buildTemplateMobileAll(subscriberId); 405 } break; 406 case ConnectivityManager.TYPE_WIFI: { 407 template = NetworkTemplate.buildTemplateWifiWildcard(); 408 } break; 409 default: { 410 throw new IllegalArgumentException("Cannot create template for network type " 411 + networkType + ", subscriberId '" 412 + NetworkIdentity.scrubSubscriberId(subscriberId) + "'."); 413 } 414 } 415 return template; 416 } 417 418 private static class CallbackHandler extends Handler { 419 private final int mNetworkType; 420 private final String mSubscriberId; 421 private UsageCallback mCallback; 422 423 CallbackHandler(Looper looper, int networkType, String subscriberId, 424 UsageCallback callback) { 425 super(looper); 426 mNetworkType = networkType; 427 mSubscriberId = subscriberId; 428 mCallback = callback; 429 } 430 431 @Override 432 public void handleMessage(Message message) { 433 DataUsageRequest request = 434 (DataUsageRequest) getObject(message, DataUsageRequest.PARCELABLE_KEY); 435 436 switch (message.what) { 437 case CALLBACK_LIMIT_REACHED: { 438 if (mCallback != null) { 439 mCallback.onThresholdReached(mNetworkType, mSubscriberId); 440 } else { 441 Log.e(TAG, "limit reached with released callback for " + request); 442 } 443 break; 444 } 445 case CALLBACK_RELEASED: { 446 if (DBG) Log.d(TAG, "callback released for " + request); 447 mCallback = null; 448 break; 449 } 450 } 451 } 452 453 private static Object getObject(Message msg, String key) { 454 return msg.getData().getParcelable(key); 455 } 456 } 457} 458