Home | History | Annotate | Download | only in usage
      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      */
    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
    131     public NetworkStatsManager(Context context, INetworkStatsService service) {
    132         mContext = context;
    133         mService = service;
    134         setPollOnOpen(true);
    135     }
    136 
    137     /** @hide */
    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
    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 */
    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 */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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      */
    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      */
    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          */
    491         public abstract void onThresholdReached(int networkType, String subscriberId);
    492 
    493         /**
    494          * @hide used for internal bookkeeping
    495          */
    496         private DataUsageRequest request;
    497     }
    498 
    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 
    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
    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 
    553         private static Object getObject(Message msg, String key) {
    554             return msg.getData().getParcelable(key);
    555         }
    556     }
    557 }
    558