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