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