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