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