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