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 android.annotation.IntDef;
     20 import android.content.Context;
     21 import android.net.INetworkStatsService;
     22 import android.net.INetworkStatsSession;
     23 import android.net.NetworkStatsHistory;
     24 import android.net.NetworkTemplate;
     25 import android.net.TrafficStats;
     26 import android.os.RemoteException;
     27 import android.os.ServiceManager;
     28 import android.util.IntArray;
     29 import android.util.Log;
     30 
     31 import dalvik.system.CloseGuard;
     32 
     33 import java.lang.annotation.Retention;
     34 import java.lang.annotation.RetentionPolicy;
     35 
     36 /**
     37  * Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
     38  * are returned as results to various queries in {@link NetworkStatsManager}.
     39  */
     40 public final class NetworkStats implements AutoCloseable {
     41     private final static String TAG = "NetworkStats";
     42 
     43     private final CloseGuard mCloseGuard = CloseGuard.get();
     44 
     45     /**
     46      * Start timestamp of stats collected
     47      */
     48     private final long mStartTimeStamp;
     49 
     50     /**
     51      * End timestamp of stats collected
     52      */
     53     private final long mEndTimeStamp;
     54 
     55     /**
     56      * Non-null array indicates the query enumerates over uids.
     57      */
     58     private int[] mUids;
     59 
     60     /**
     61      * Index of the current uid in mUids when doing uid enumeration or a single uid value,
     62      * depending on query type.
     63      */
     64     private int mUidOrUidIndex;
     65 
     66     /**
     67      * Tag id in case if was specified in the query.
     68      */
     69     private int mTag = android.net.NetworkStats.TAG_NONE;
     70 
     71     /**
     72      * The session while the query requires it, null if all the stats have been collected or close()
     73      * has been called.
     74      */
     75     private INetworkStatsSession mSession;
     76     private NetworkTemplate mTemplate;
     77 
     78     /**
     79      * Results of a summary query.
     80      */
     81     private android.net.NetworkStats mSummary = null;
     82 
     83     /**
     84      * Results of detail queries.
     85      */
     86     private NetworkStatsHistory mHistory = null;
     87 
     88     /**
     89      * Where we are in enumerating over the current result.
     90      */
     91     private int mEnumerationIndex = 0;
     92 
     93     /**
     94      * Recycling entry objects to prevent heap fragmentation.
     95      */
     96     private android.net.NetworkStats.Entry mRecycledSummaryEntry = null;
     97     private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
     98 
     99     /** @hide */
    100     NetworkStats(Context context, NetworkTemplate template, long startTimestamp,
    101             long endTimestamp) throws RemoteException, SecurityException {
    102         final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
    103                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
    104         // Open network stats session
    105         mSession = statsService.openSessionForUsageStats(context.getOpPackageName());
    106         mCloseGuard.open("close");
    107         mTemplate = template;
    108         mStartTimeStamp = startTimestamp;
    109         mEndTimeStamp = endTimestamp;
    110     }
    111 
    112     @Override
    113     protected void finalize() throws Throwable {
    114         try {
    115             if (mCloseGuard != null) {
    116                 mCloseGuard.warnIfOpen();
    117             }
    118             close();
    119         } finally {
    120             super.finalize();
    121         }
    122     }
    123 
    124     // -------------------------BEGINNING OF PUBLIC API-----------------------------------
    125 
    126     /**
    127      * Buckets are the smallest elements of a query result. As some dimensions of a result may be
    128      * aggregated (e.g. time or state) some values may be equal across all buckets.
    129      */
    130     public static class Bucket {
    131         /** @hide */
    132         @IntDef({STATE_ALL, STATE_DEFAULT, STATE_FOREGROUND})
    133         @Retention(RetentionPolicy.SOURCE)
    134         public @interface State {}
    135 
    136         /**
    137          * Combined usage across all states.
    138          */
    139         public static final int STATE_ALL = -1;
    140 
    141         /**
    142          * Usage not accounted for in any other state.
    143          */
    144         public static final int STATE_DEFAULT = 0x1;
    145 
    146         /**
    147          * Foreground usage.
    148          */
    149         public static final int STATE_FOREGROUND = 0x2;
    150 
    151         /**
    152          * Special UID value for aggregate/unspecified.
    153          */
    154         public static final int UID_ALL = android.net.NetworkStats.UID_ALL;
    155 
    156         /**
    157          * Special UID value for removed apps.
    158          */
    159         public static final int UID_REMOVED = TrafficStats.UID_REMOVED;
    160 
    161         /**
    162          * Special UID value for data usage by tethering.
    163          */
    164         public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
    165 
    166         /** @hide */
    167         @IntDef({METERED_ALL, METERED_NO, METERED_YES})
    168         @Retention(RetentionPolicy.SOURCE)
    169         public @interface Metered {}
    170 
    171         /**
    172          * Combined usage across all metered states. Covers metered and unmetered usage.
    173          */
    174         public static final int METERED_ALL = -1;
    175 
    176         /**
    177          * Usage that occurs on an unmetered network.
    178          */
    179         public static final int METERED_NO = 0x1;
    180 
    181         /**
    182          * Usage that occurs on a metered network.
    183          *
    184          * <p>A network is classified as metered when the user is sensitive to heavy data usage on
    185          * that connection.
    186          */
    187         public static final int METERED_YES = 0x2;
    188 
    189         /** @hide */
    190         @IntDef({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
    191         @Retention(RetentionPolicy.SOURCE)
    192         public @interface Roaming {}
    193 
    194         /**
    195          * Combined usage across all roaming states. Covers both roaming and non-roaming usage.
    196          */
    197         public static final int ROAMING_ALL = -1;
    198 
    199         /**
    200          * Usage that occurs on a home, non-roaming network.
    201          *
    202          * <p>Any cellular usage in this bucket was incurred while the device was connected to a
    203          * tower owned or operated by the user's wireless carrier, or a tower that the user's
    204          * wireless carrier has indicated should be treated as a home network regardless.
    205          *
    206          * <p>This is also the default value for network types that do not support roaming.
    207          */
    208         public static final int ROAMING_NO = 0x1;
    209 
    210         /**
    211          * Usage that occurs on a roaming network.
    212          *
    213          * <p>Any cellular usage in this bucket as incurred while the device was roaming on another
    214          * carrier's network, for which additional charges may apply.
    215          */
    216         public static final int ROAMING_YES = 0x2;
    217 
    218         /**
    219          * Special TAG value for total data across all tags
    220          */
    221         public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE;
    222 
    223         private int mUid;
    224         private int mTag;
    225         private int mState;
    226         private int mMetered;
    227         private int mRoaming;
    228         private long mBeginTimeStamp;
    229         private long mEndTimeStamp;
    230         private long mRxBytes;
    231         private long mRxPackets;
    232         private long mTxBytes;
    233         private long mTxPackets;
    234 
    235         private static @State int convertState(int networkStatsSet) {
    236             switch (networkStatsSet) {
    237                 case android.net.NetworkStats.SET_ALL : return STATE_ALL;
    238                 case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
    239                 case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
    240             }
    241             return 0;
    242         }
    243 
    244         private static int convertUid(int uid) {
    245             switch (uid) {
    246                 case TrafficStats.UID_REMOVED: return UID_REMOVED;
    247                 case TrafficStats.UID_TETHERING: return UID_TETHERING;
    248             }
    249             return uid;
    250         }
    251 
    252         private static int convertTag(int tag) {
    253             switch (tag) {
    254                 case android.net.NetworkStats.TAG_NONE: return TAG_NONE;
    255             }
    256             return tag;
    257         }
    258 
    259         private static @Metered int convertMetered(int metered) {
    260             switch (metered) {
    261                 case android.net.NetworkStats.METERED_ALL : return METERED_ALL;
    262                 case android.net.NetworkStats.METERED_NO: return METERED_NO;
    263                 case android.net.NetworkStats.METERED_YES: return METERED_YES;
    264             }
    265             return 0;
    266         }
    267 
    268         private static @Roaming int convertRoaming(int roaming) {
    269             switch (roaming) {
    270                 case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
    271                 case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO;
    272                 case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES;
    273             }
    274             return 0;
    275         }
    276 
    277         public Bucket() {
    278         }
    279 
    280         /**
    281          * Key of the bucket. Usually an app uid or one of the following special values:<p />
    282          * <ul>
    283          * <li>{@link #UID_REMOVED}</li>
    284          * <li>{@link #UID_TETHERING}</li>
    285          * <li>{@link android.os.Process#SYSTEM_UID}</li>
    286          * </ul>
    287          * @return Bucket key.
    288          */
    289         public int getUid() {
    290             return mUid;
    291         }
    292 
    293         /**
    294          * Tag of the bucket.<p />
    295          * @return Bucket tag.
    296          */
    297         public int getTag() {
    298             return mTag;
    299         }
    300 
    301         /**
    302          * Usage state. One of the following values:<p/>
    303          * <ul>
    304          * <li>{@link #STATE_ALL}</li>
    305          * <li>{@link #STATE_DEFAULT}</li>
    306          * <li>{@link #STATE_FOREGROUND}</li>
    307          * </ul>
    308          * @return Usage state.
    309          */
    310         public @State int getState() {
    311             return mState;
    312         }
    313 
    314         /**
    315          * Metered state. One of the following values:<p/>
    316          * <ul>
    317          * <li>{@link #METERED_ALL}</li>
    318          * <li>{@link #METERED_NO}</li>
    319          * <li>{@link #METERED_YES}</li>
    320          * </ul>
    321          * <p>A network is classified as metered when the user is sensitive to heavy data usage on
    322          * that connection. Apps may warn before using these networks for large downloads. The
    323          * metered state can be set by the user within data usage network restrictions.
    324          */
    325         public @Metered int getMetered() {
    326             return mMetered;
    327         }
    328 
    329         /**
    330          * Roaming state. One of the following values:<p/>
    331          * <ul>
    332          * <li>{@link #ROAMING_ALL}</li>
    333          * <li>{@link #ROAMING_NO}</li>
    334          * <li>{@link #ROAMING_YES}</li>
    335          * </ul>
    336          */
    337         public @Roaming int getRoaming() {
    338             return mRoaming;
    339         }
    340 
    341         /**
    342          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
    343          * {@link java.lang.System#currentTimeMillis}.
    344          * @return Start of interval.
    345          */
    346         public long getStartTimeStamp() {
    347             return mBeginTimeStamp;
    348         }
    349 
    350         /**
    351          * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
    352          * {@link java.lang.System#currentTimeMillis}.
    353          * @return End of interval.
    354          */
    355         public long getEndTimeStamp() {
    356             return mEndTimeStamp;
    357         }
    358 
    359         /**
    360          * Number of bytes received during the bucket's time interval. Statistics are measured at
    361          * the network layer, so they include both TCP and UDP usage.
    362          * @return Number of bytes.
    363          */
    364         public long getRxBytes() {
    365             return mRxBytes;
    366         }
    367 
    368         /**
    369          * Number of bytes transmitted during the bucket's time interval. Statistics are measured at
    370          * the network layer, so they include both TCP and UDP usage.
    371          * @return Number of bytes.
    372          */
    373         public long getTxBytes() {
    374             return mTxBytes;
    375         }
    376 
    377         /**
    378          * Number of packets received during the bucket's time interval. Statistics are measured at
    379          * the network layer, so they include both TCP and UDP usage.
    380          * @return Number of packets.
    381          */
    382         public long getRxPackets() {
    383             return mRxPackets;
    384         }
    385 
    386         /**
    387          * Number of packets transmitted during the bucket's time interval. Statistics are measured
    388          * at the network layer, so they include both TCP and UDP usage.
    389          * @return Number of packets.
    390          */
    391         public long getTxPackets() {
    392             return mTxPackets;
    393         }
    394     }
    395 
    396     /**
    397      * Fills the recycled bucket with data of the next bin in the enumeration.
    398      * @param bucketOut Bucket to be filled with data.
    399      * @return true if successfully filled the bucket, false otherwise.
    400      */
    401     public boolean getNextBucket(Bucket bucketOut) {
    402         if (mSummary != null) {
    403             return getNextSummaryBucket(bucketOut);
    404         } else {
    405             return getNextHistoryBucket(bucketOut);
    406         }
    407     }
    408 
    409     /**
    410      * Check if it is possible to ask for a next bucket in the enumeration.
    411      * @return true if there is at least one more bucket.
    412      */
    413     public boolean hasNextBucket() {
    414         if (mSummary != null) {
    415             return mEnumerationIndex < mSummary.size();
    416         } else if (mHistory != null) {
    417             return mEnumerationIndex < mHistory.size()
    418                     || hasNextUid();
    419         }
    420         return false;
    421     }
    422 
    423     /**
    424      * Closes the enumeration. Call this method before this object gets out of scope.
    425      */
    426     @Override
    427     public void close() {
    428         if (mSession != null) {
    429             try {
    430                 mSession.close();
    431             } catch (RemoteException e) {
    432                 Log.w(TAG, e);
    433                 // Otherwise, meh
    434             }
    435         }
    436         mSession = null;
    437         if (mCloseGuard != null) {
    438             mCloseGuard.close();
    439         }
    440     }
    441 
    442     // -------------------------END OF PUBLIC API-----------------------------------
    443 
    444     /**
    445      * Collects device summary results into a Bucket.
    446      * @throws RemoteException
    447      */
    448     Bucket getDeviceSummaryForNetwork() throws RemoteException {
    449         mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp);
    450 
    451         // Setting enumeration index beyond end to avoid accidental enumeration over data that does
    452         // not belong to the calling user.
    453         mEnumerationIndex = mSummary.size();
    454 
    455         return getSummaryAggregate();
    456     }
    457 
    458     /**
    459      * Collects summary results and sets summary enumeration mode.
    460      * @throws RemoteException
    461      */
    462     void startSummaryEnumeration() throws RemoteException {
    463         mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp,
    464                 false /* includeTags */);
    465         mEnumerationIndex = 0;
    466     }
    467 
    468     /**
    469      * Collects history results for uid and resets history enumeration index.
    470      */
    471     void startHistoryEnumeration(int uid) {
    472         startHistoryEnumeration(uid, android.net.NetworkStats.TAG_NONE);
    473     }
    474 
    475     /**
    476      * Collects history results for uid and resets history enumeration index.
    477      */
    478     void startHistoryEnumeration(int uid, int tag) {
    479         mHistory = null;
    480         try {
    481             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
    482                     android.net.NetworkStats.SET_ALL, tag,
    483                     NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
    484             setSingleUidTag(uid, tag);
    485         } catch (RemoteException e) {
    486             Log.w(TAG, e);
    487             // Leaving mHistory null
    488         }
    489         mEnumerationIndex = 0;
    490     }
    491 
    492     /**
    493      * Starts uid enumeration for current user.
    494      * @throws RemoteException
    495      */
    496     void startUserUidEnumeration() throws RemoteException {
    497         // TODO: getRelevantUids should be sensitive to time interval. When that's done,
    498         //       the filtering logic below can be removed.
    499         int[] uids = mSession.getRelevantUids();
    500         // Filtering of uids with empty history.
    501         IntArray filteredUids = new IntArray(uids.length);
    502         for (int uid : uids) {
    503             try {
    504                 NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
    505                         android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
    506                         NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
    507                 if (history != null && history.size() > 0) {
    508                     filteredUids.add(uid);
    509                 }
    510             } catch (RemoteException e) {
    511                 Log.w(TAG, "Error while getting history of uid " + uid, e);
    512             }
    513         }
    514         mUids = filteredUids.toArray();
    515         mUidOrUidIndex = -1;
    516         stepHistory();
    517     }
    518 
    519     /**
    520      * Steps to next uid in enumeration and collects history for that.
    521      */
    522     private void stepHistory(){
    523         if (hasNextUid()) {
    524             stepUid();
    525             mHistory = null;
    526             try {
    527                 mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(),
    528                         android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
    529                         NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
    530             } catch (RemoteException e) {
    531                 Log.w(TAG, e);
    532                 // Leaving mHistory null
    533             }
    534             mEnumerationIndex = 0;
    535         }
    536     }
    537 
    538     private void fillBucketFromSummaryEntry(Bucket bucketOut) {
    539         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
    540         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
    541         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
    542         bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
    543         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
    544         bucketOut.mBeginTimeStamp = mStartTimeStamp;
    545         bucketOut.mEndTimeStamp = mEndTimeStamp;
    546         bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
    547         bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
    548         bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
    549         bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
    550     }
    551 
    552     /**
    553      * Getting the next item in summary enumeration.
    554      * @param bucketOut Next item will be set here.
    555      * @return true if a next item could be set.
    556      */
    557     private boolean getNextSummaryBucket(Bucket bucketOut) {
    558         if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
    559             mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
    560             fillBucketFromSummaryEntry(bucketOut);
    561             return true;
    562         }
    563         return false;
    564     }
    565 
    566     Bucket getSummaryAggregate() {
    567         if (mSummary == null) {
    568             return null;
    569         }
    570         Bucket bucket = new Bucket();
    571         if (mRecycledSummaryEntry == null) {
    572             mRecycledSummaryEntry = new android.net.NetworkStats.Entry();
    573         }
    574         mSummary.getTotal(mRecycledSummaryEntry);
    575         fillBucketFromSummaryEntry(bucket);
    576         return bucket;
    577     }
    578     /**
    579      * Getting the next item in a history enumeration.
    580      * @param bucketOut Next item will be set here.
    581      * @return true if a next item could be set.
    582      */
    583     private boolean getNextHistoryBucket(Bucket bucketOut) {
    584         if (bucketOut != null && mHistory != null) {
    585             if (mEnumerationIndex < mHistory.size()) {
    586                 mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
    587                         mRecycledHistoryEntry);
    588                 bucketOut.mUid = Bucket.convertUid(getUid());
    589                 bucketOut.mTag = Bucket.convertTag(mTag);
    590                 bucketOut.mState = Bucket.STATE_ALL;
    591                 bucketOut.mMetered = Bucket.METERED_ALL;
    592                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
    593                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
    594                 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
    595                         mRecycledHistoryEntry.bucketDuration;
    596                 bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
    597                 bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
    598                 bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
    599                 bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
    600                 return true;
    601             } else if (hasNextUid()) {
    602                 stepHistory();
    603                 return getNextHistoryBucket(bucketOut);
    604             }
    605         }
    606         return false;
    607     }
    608 
    609     // ------------------ UID LOGIC------------------------
    610 
    611     private boolean isUidEnumeration() {
    612         return mUids != null;
    613     }
    614 
    615     private boolean hasNextUid() {
    616         return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
    617     }
    618 
    619     private int getUid() {
    620         // Check if uid enumeration.
    621         if (isUidEnumeration()) {
    622             if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
    623                 throw new IndexOutOfBoundsException(
    624                         "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
    625             }
    626             return mUids[mUidOrUidIndex];
    627         }
    628         // Single uid mode.
    629         return mUidOrUidIndex;
    630     }
    631 
    632     private void setSingleUidTag(int uid, int tag) {
    633         mUidOrUidIndex = uid;
    634         mTag = tag;
    635     }
    636 
    637     private void stepUid() {
    638         if (mUids != null) {
    639             ++mUidOrUidIndex;
    640         }
    641     }
    642 }
    643