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({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
    168         @Retention(RetentionPolicy.SOURCE)
    169         public @interface Roaming {}
    170 
    171         /**
    172          * Combined usage across all roaming states. Covers both roaming and non-roaming usage.
    173          */
    174         public static final int ROAMING_ALL = -1;
    175 
    176         /**
    177          * Usage that occurs on a home, non-roaming network.
    178          *
    179          * <p>Any cellular usage in this bucket was incurred while the device was connected to a
    180          * tower owned or operated by the user's wireless carrier, or a tower that the user's
    181          * wireless carrier has indicated should be treated as a home network regardless.
    182          *
    183          * <p>This is also the default value for network types that do not support roaming.
    184          */
    185         public static final int ROAMING_NO = 0x1;
    186 
    187         /**
    188          * Usage that occurs on a roaming network.
    189          *
    190          * <p>Any cellular usage in this bucket as incurred while the device was roaming on another
    191          * carrier's network, for which additional charges may apply.
    192          */
    193         public static final int ROAMING_YES = 0x2;
    194 
    195         /**
    196          * Special TAG value for total data across all tags
    197          */
    198         public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE;
    199 
    200         private int mUid;
    201         private int mTag;
    202         private int mState;
    203         private int mRoaming;
    204         private long mBeginTimeStamp;
    205         private long mEndTimeStamp;
    206         private long mRxBytes;
    207         private long mRxPackets;
    208         private long mTxBytes;
    209         private long mTxPackets;
    210 
    211         private static @State int convertState(int networkStatsSet) {
    212             switch (networkStatsSet) {
    213                 case android.net.NetworkStats.SET_ALL : return STATE_ALL;
    214                 case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
    215                 case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
    216             }
    217             return 0;
    218         }
    219 
    220         private static int convertUid(int uid) {
    221             switch (uid) {
    222                 case TrafficStats.UID_REMOVED: return UID_REMOVED;
    223                 case TrafficStats.UID_TETHERING: return UID_TETHERING;
    224             }
    225             return uid;
    226         }
    227 
    228         private static int convertTag(int tag) {
    229             switch (tag) {
    230                 case android.net.NetworkStats.TAG_NONE: return TAG_NONE;
    231             }
    232             return tag;
    233         }
    234 
    235         private static @Roaming int convertRoaming(int roaming) {
    236             switch (roaming) {
    237                 case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
    238                 case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO;
    239                 case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES;
    240             }
    241             return 0;
    242         }
    243 
    244         public Bucket() {
    245         }
    246 
    247         /**
    248          * Key of the bucket. Usually an app uid or one of the following special values:<p />
    249          * <ul>
    250          * <li>{@link #UID_REMOVED}</li>
    251          * <li>{@link #UID_TETHERING}</li>
    252          * <li>{@link android.os.Process#SYSTEM_UID}</li>
    253          * </ul>
    254          * @return Bucket key.
    255          */
    256         public int getUid() {
    257             return mUid;
    258         }
    259 
    260         /**
    261          * Tag of the bucket.<p />
    262          * @return Bucket tag.
    263          */
    264         public int getTag() {
    265             return mTag;
    266         }
    267 
    268         /**
    269          * Usage state. One of the following values:<p/>
    270          * <ul>
    271          * <li>{@link #STATE_ALL}</li>
    272          * <li>{@link #STATE_DEFAULT}</li>
    273          * <li>{@link #STATE_FOREGROUND}</li>
    274          * </ul>
    275          * @return Usage state.
    276          */
    277         public @State int getState() {
    278             return mState;
    279         }
    280 
    281         /**
    282          * Roaming state. One of the following values:<p/>
    283          * <ul>
    284          * <li>{@link #ROAMING_ALL}</li>
    285          * <li>{@link #ROAMING_NO}</li>
    286          * <li>{@link #ROAMING_YES}</li>
    287          * </ul>
    288          */
    289         public @Roaming int getRoaming() {
    290             return mRoaming;
    291         }
    292 
    293         /**
    294          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
    295          * {@link java.lang.System#currentTimeMillis}.
    296          * @return Start of interval.
    297          */
    298         public long getStartTimeStamp() {
    299             return mBeginTimeStamp;
    300         }
    301 
    302         /**
    303          * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
    304          * {@link java.lang.System#currentTimeMillis}.
    305          * @return End of interval.
    306          */
    307         public long getEndTimeStamp() {
    308             return mEndTimeStamp;
    309         }
    310 
    311         /**
    312          * Number of bytes received during the bucket's time interval. Statistics are measured at
    313          * the network layer, so they include both TCP and UDP usage.
    314          * @return Number of bytes.
    315          */
    316         public long getRxBytes() {
    317             return mRxBytes;
    318         }
    319 
    320         /**
    321          * Number of bytes transmitted during the bucket's time interval. Statistics are measured at
    322          * the network layer, so they include both TCP and UDP usage.
    323          * @return Number of bytes.
    324          */
    325         public long getTxBytes() {
    326             return mTxBytes;
    327         }
    328 
    329         /**
    330          * Number of packets received during the bucket's time interval. Statistics are measured at
    331          * the network layer, so they include both TCP and UDP usage.
    332          * @return Number of packets.
    333          */
    334         public long getRxPackets() {
    335             return mRxPackets;
    336         }
    337 
    338         /**
    339          * Number of packets transmitted during the bucket's time interval. Statistics are measured
    340          * at the network layer, so they include both TCP and UDP usage.
    341          * @return Number of packets.
    342          */
    343         public long getTxPackets() {
    344             return mTxPackets;
    345         }
    346     }
    347 
    348     /**
    349      * Fills the recycled bucket with data of the next bin in the enumeration.
    350      * @param bucketOut Bucket to be filled with data.
    351      * @return true if successfully filled the bucket, false otherwise.
    352      */
    353     public boolean getNextBucket(Bucket bucketOut) {
    354         if (mSummary != null) {
    355             return getNextSummaryBucket(bucketOut);
    356         } else {
    357             return getNextHistoryBucket(bucketOut);
    358         }
    359     }
    360 
    361     /**
    362      * Check if it is possible to ask for a next bucket in the enumeration.
    363      * @return true if there is at least one more bucket.
    364      */
    365     public boolean hasNextBucket() {
    366         if (mSummary != null) {
    367             return mEnumerationIndex < mSummary.size();
    368         } else if (mHistory != null) {
    369             return mEnumerationIndex < mHistory.size()
    370                     || hasNextUid();
    371         }
    372         return false;
    373     }
    374 
    375     /**
    376      * Closes the enumeration. Call this method before this object gets out of scope.
    377      */
    378     @Override
    379     public void close() {
    380         if (mSession != null) {
    381             try {
    382                 mSession.close();
    383             } catch (RemoteException e) {
    384                 Log.w(TAG, e);
    385                 // Otherwise, meh
    386             }
    387         }
    388         mSession = null;
    389         if (mCloseGuard != null) {
    390             mCloseGuard.close();
    391         }
    392     }
    393 
    394     // -------------------------END OF PUBLIC API-----------------------------------
    395 
    396     /**
    397      * Collects device summary results into a Bucket.
    398      * @throws RemoteException
    399      */
    400     Bucket getDeviceSummaryForNetwork() throws RemoteException {
    401         mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp);
    402 
    403         // Setting enumeration index beyond end to avoid accidental enumeration over data that does
    404         // not belong to the calling user.
    405         mEnumerationIndex = mSummary.size();
    406 
    407         return getSummaryAggregate();
    408     }
    409 
    410     /**
    411      * Collects summary results and sets summary enumeration mode.
    412      * @throws RemoteException
    413      */
    414     void startSummaryEnumeration() throws RemoteException {
    415         mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp,
    416                 false /* includeTags */);
    417         mEnumerationIndex = 0;
    418     }
    419 
    420     /**
    421      * Collects history results for uid and resets history enumeration index.
    422      */
    423     void startHistoryEnumeration(int uid) {
    424         startHistoryEnumeration(uid, android.net.NetworkStats.TAG_NONE);
    425     }
    426 
    427     /**
    428      * Collects history results for uid and resets history enumeration index.
    429      */
    430     void startHistoryEnumeration(int uid, int tag) {
    431         mHistory = null;
    432         try {
    433             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
    434                     android.net.NetworkStats.SET_ALL, tag,
    435                     NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
    436             setSingleUidTag(uid, tag);
    437         } catch (RemoteException e) {
    438             Log.w(TAG, e);
    439             // Leaving mHistory null
    440         }
    441         mEnumerationIndex = 0;
    442     }
    443 
    444     /**
    445      * Starts uid enumeration for current user.
    446      * @throws RemoteException
    447      */
    448     void startUserUidEnumeration() throws RemoteException {
    449         // TODO: getRelevantUids should be sensitive to time interval. When that's done,
    450         //       the filtering logic below can be removed.
    451         int[] uids = mSession.getRelevantUids();
    452         // Filtering of uids with empty history.
    453         IntArray filteredUids = new IntArray(uids.length);
    454         for (int uid : uids) {
    455             try {
    456                 NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
    457                         android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
    458                         NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
    459                 if (history != null && history.size() > 0) {
    460                     filteredUids.add(uid);
    461                 }
    462             } catch (RemoteException e) {
    463                 Log.w(TAG, "Error while getting history of uid " + uid, e);
    464             }
    465         }
    466         mUids = filteredUids.toArray();
    467         mUidOrUidIndex = -1;
    468         stepHistory();
    469     }
    470 
    471     /**
    472      * Steps to next uid in enumeration and collects history for that.
    473      */
    474     private void stepHistory(){
    475         if (hasNextUid()) {
    476             stepUid();
    477             mHistory = null;
    478             try {
    479                 mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(),
    480                         android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
    481                         NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
    482             } catch (RemoteException e) {
    483                 Log.w(TAG, e);
    484                 // Leaving mHistory null
    485             }
    486             mEnumerationIndex = 0;
    487         }
    488     }
    489 
    490     private void fillBucketFromSummaryEntry(Bucket bucketOut) {
    491         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
    492         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
    493         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
    494         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
    495         bucketOut.mBeginTimeStamp = mStartTimeStamp;
    496         bucketOut.mEndTimeStamp = mEndTimeStamp;
    497         bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
    498         bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
    499         bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
    500         bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
    501     }
    502 
    503     /**
    504      * Getting the next item in summary enumeration.
    505      * @param bucketOut Next item will be set here.
    506      * @return true if a next item could be set.
    507      */
    508     private boolean getNextSummaryBucket(Bucket bucketOut) {
    509         if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
    510             mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
    511             fillBucketFromSummaryEntry(bucketOut);
    512             return true;
    513         }
    514         return false;
    515     }
    516 
    517     Bucket getSummaryAggregate() {
    518         if (mSummary == null) {
    519             return null;
    520         }
    521         Bucket bucket = new Bucket();
    522         if (mRecycledSummaryEntry == null) {
    523             mRecycledSummaryEntry = new android.net.NetworkStats.Entry();
    524         }
    525         mSummary.getTotal(mRecycledSummaryEntry);
    526         fillBucketFromSummaryEntry(bucket);
    527         return bucket;
    528     }
    529     /**
    530      * Getting the next item in a history enumeration.
    531      * @param bucketOut Next item will be set here.
    532      * @return true if a next item could be set.
    533      */
    534     private boolean getNextHistoryBucket(Bucket bucketOut) {
    535         if (bucketOut != null && mHistory != null) {
    536             if (mEnumerationIndex < mHistory.size()) {
    537                 mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
    538                         mRecycledHistoryEntry);
    539                 bucketOut.mUid = Bucket.convertUid(getUid());
    540                 bucketOut.mTag = Bucket.convertTag(mTag);
    541                 bucketOut.mState = Bucket.STATE_ALL;
    542                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
    543                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
    544                 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
    545                         mRecycledHistoryEntry.bucketDuration;
    546                 bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
    547                 bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
    548                 bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
    549                 bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
    550                 return true;
    551             } else if (hasNextUid()) {
    552                 stepHistory();
    553                 return getNextHistoryBucket(bucketOut);
    554             }
    555         }
    556         return false;
    557     }
    558 
    559     // ------------------ UID LOGIC------------------------
    560 
    561     private boolean isUidEnumeration() {
    562         return mUids != null;
    563     }
    564 
    565     private boolean hasNextUid() {
    566         return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
    567     }
    568 
    569     private int getUid() {
    570         // Check if uid enumeration.
    571         if (isUidEnumeration()) {
    572             if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
    573                 throw new IndexOutOfBoundsException(
    574                         "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
    575             }
    576             return mUids[mUidOrUidIndex];
    577         }
    578         // Single uid mode.
    579         return mUidOrUidIndex;
    580     }
    581 
    582     private void setSingleUidTag(int uid, int tag) {
    583         mUidOrUidIndex = uid;
    584         mTag = tag;
    585     }
    586 
    587     private void stepUid() {
    588         if (mUids != null) {
    589             ++mUidOrUidIndex;
    590         }
    591     }
    592 }
    593