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