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