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