All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.app.usage.NetworkStats Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/**
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package android.app.usage;

import android.annotation.IntDef;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
import android.util.IntArray;
import android.util.Log;

import dalvik.system.CloseGuard;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
 * are returned as results to various queries in {@link NetworkStatsManager}.
 */
public final class NetworkStats implements AutoCloseable {
    private final static String TAG = "NetworkStats";

    private final CloseGuard mCloseGuard = CloseGuard.get();

    /**
     * Start timestamp of stats collected
     */
    private final long mStartTimeStamp;

    /**
     * End timestamp of stats collected
     */
    private final long mEndTimeStamp;

    /**
     * Non-null array indicates the query enumerates over uids.
     */
    private int[] mUids;

    /**
     * Index of the current uid in mUids when doing uid enumeration or a single uid value,
     * depending on query type.
     */
    private int mUidOrUidIndex;

    /**
     * Tag id in case if was specified in the query.
     */
    private int mTag = android.net.NetworkStats.TAG_NONE;

    /**
     * State in case it was not specified in the query.
     */
    private int mState = Bucket.STATE_ALL;

    /**
     * The session while the query requires it, null if all the stats have been collected or close()
     * has been called.
     */
    private INetworkStatsSession mSession;
    private NetworkTemplate mTemplate;

    /**
     * Results of a summary query.
     */
    private android.net.NetworkStats mSummary = null;

    /**
     * Results of detail queries.
     */
    private NetworkStatsHistory mHistory = null;

    /**
     * Where we are in enumerating over the current result.
     */
    private int mEnumerationIndex = 0;

    /**
     * Recycling entry objects to prevent heap fragmentation.
     */
    private android.net.NetworkStats.Entry mRecycledSummaryEntry = null;
    private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;

    /** @hide */
    NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
            long endTimestamp, INetworkStatsService statsService)
            throws RemoteException, SecurityException {
        // Open network stats session
        mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
        mCloseGuard.open("close");
        mTemplate = template;
        mStartTimeStamp = startTimestamp;
        mEndTimeStamp = endTimestamp;
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (mCloseGuard != null) {
                mCloseGuard.warnIfOpen();
            }
            close();
        } finally {
            super.finalize();
        }
    }

    // -------------------------BEGINNING OF PUBLIC API-----------------------------------

    /**
     * Buckets are the smallest elements of a query result. As some dimensions of a result may be
     * aggregated (e.g. time or state) some values may be equal across all buckets.
     */
    public static class Bucket {
        /** @hide */
        @IntDef(prefix = { "STATE_" }, value = {
                STATE_ALL,
                STATE_DEFAULT,
                STATE_FOREGROUND
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface State {}

        /**
         * Combined usage across all states.
         */
        public static final int STATE_ALL = -1;

        /**
         * Usage not accounted for in any other state.
         */
        public static final int STATE_DEFAULT = 0x1;

        /**
         * Foreground usage.
         */
        public static final int STATE_FOREGROUND = 0x2;

        /**
         * Special UID value for aggregate/unspecified.
         */
        public static final int UID_ALL = android.net.NetworkStats.UID_ALL;

        /**
         * Special UID value for removed apps.
         */
        public static final int UID_REMOVED = TrafficStats.UID_REMOVED;

        /**
         * Special UID value for data usage by tethering.
         */
        public static final int UID_TETHERING = TrafficStats.UID_TETHERING;

        /** @hide */
        @IntDef(prefix = { "METERED_" }, value = {
                METERED_ALL,
                METERED_NO,
                METERED_YES
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface Metered {}

        /**
         * Combined usage across all metered states. Covers metered and unmetered usage.
         */
        public static final int METERED_ALL = -1;

        /**
         * Usage that occurs on an unmetered network.
         */
        public static final int METERED_NO = 0x1;

        /**
         * Usage that occurs on a metered network.
         *
         * 

A network is classified as metered when the user is sensitive to heavy data usage on * that connection. */ public static final int METERED_YES = 0x2; /** @hide */ @IntDef(prefix = { "ROAMING_" }, value = { ROAMING_ALL, ROAMING_NO, ROAMING_YES }) @Retention(RetentionPolicy.SOURCE) public @interface Roaming {} /** * Combined usage across all roaming states. Covers both roaming and non-roaming usage. */ public static final int ROAMING_ALL = -1; /** * Usage that occurs on a home, non-roaming network. * *

Any cellular usage in this bucket was incurred while the device was connected to a * tower owned or operated by the user's wireless carrier, or a tower that the user's * wireless carrier has indicated should be treated as a home network regardless. * *

This is also the default value for network types that do not support roaming. */ public static final int ROAMING_NO = 0x1; /** * Usage that occurs on a roaming network. * *

Any cellular usage in this bucket as incurred while the device was roaming on another * carrier's network, for which additional charges may apply. */ public static final int ROAMING_YES = 0x2; /** @hide */ @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_NO, DEFAULT_NETWORK_YES }) @Retention(RetentionPolicy.SOURCE) public @interface DefaultNetworkStatus {} /** * Combined usage for this network regardless of default network status. */ public static final int DEFAULT_NETWORK_ALL = -1; /** * Usage that occurs while this network is not a default network. * *

This implies that the app responsible for this usage requested that it occur on a * specific network different from the one(s) the system would have selected for it. */ public static final int DEFAULT_NETWORK_NO = 0x1; /** * Usage that occurs while this network is a default network. * *

This implies that the app either did not select a specific network for this usage, * or it selected a network that the system could have selected for app traffic. */ public static final int DEFAULT_NETWORK_YES = 0x2; /** * Special TAG value for total data across all tags */ public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE; private int mUid; private int mTag; private int mState; private int mDefaultNetworkStatus; private int mMetered; private int mRoaming; private long mBeginTimeStamp; private long mEndTimeStamp; private long mRxBytes; private long mRxPackets; private long mTxBytes; private long mTxPackets; private static int convertSet(@State int state) { switch (state) { case STATE_ALL: return android.net.NetworkStats.SET_ALL; case STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT; case STATE_FOREGROUND: return android.net.NetworkStats.SET_FOREGROUND; } return 0; } private static @State int convertState(int networkStatsSet) { switch (networkStatsSet) { case android.net.NetworkStats.SET_ALL : return STATE_ALL; case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT; case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND; } return 0; } private static int convertUid(int uid) { switch (uid) { case TrafficStats.UID_REMOVED: return UID_REMOVED; case TrafficStats.UID_TETHERING: return UID_TETHERING; } return uid; } private static int convertTag(int tag) { switch (tag) { case android.net.NetworkStats.TAG_NONE: return TAG_NONE; } return tag; } private static @Metered int convertMetered(int metered) { switch (metered) { case android.net.NetworkStats.METERED_ALL : return METERED_ALL; case android.net.NetworkStats.METERED_NO: return METERED_NO; case android.net.NetworkStats.METERED_YES: return METERED_YES; } return 0; } private static @Roaming int convertRoaming(int roaming) { switch (roaming) { case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL; case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO; case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES; } return 0; } private static @DefaultNetworkStatus int convertDefaultNetworkStatus( int defaultNetworkStatus) { switch (defaultNetworkStatus) { case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL; case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO; case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES; } return 0; } public Bucket() { } /** * Key of the bucket. Usually an app uid or one of the following special values:

*

    *
  • {@link #UID_REMOVED}
  • *
  • {@link #UID_TETHERING}
  • *
  • {@link android.os.Process#SYSTEM_UID}
  • *
* @return Bucket key. */ public int getUid() { return mUid; } /** * Tag of the bucket.

* @return Bucket tag. */ public int getTag() { return mTag; } /** * Usage state. One of the following values:

*

    *
  • {@link #STATE_ALL}
  • *
  • {@link #STATE_DEFAULT}
  • *
  • {@link #STATE_FOREGROUND}
  • *
* @return Usage state. */ public @State int getState() { return mState; } /** * Metered state. One of the following values:

*

    *
  • {@link #METERED_ALL}
  • *
  • {@link #METERED_NO}
  • *
  • {@link #METERED_YES}
  • *
*

A network is classified as metered when the user is sensitive to heavy data usage on * that connection. Apps may warn before using these networks for large downloads. The * metered state can be set by the user within data usage network restrictions. */ public @Metered int getMetered() { return mMetered; } /** * Roaming state. One of the following values:

*

    *
  • {@link #ROAMING_ALL}
  • *
  • {@link #ROAMING_NO}
  • *
  • {@link #ROAMING_YES}
  • *
*/ public @Roaming int getRoaming() { return mRoaming; } /** * Default network status. One of the following values:

*

    *
  • {@link #DEFAULT_NETWORK_ALL}
  • *
  • {@link #DEFAULT_NETWORK_NO}
  • *
  • {@link #DEFAULT_NETWORK_YES}
  • *
*/ public @DefaultNetworkStatus int getDefaultNetworkStatus() { return mDefaultNetworkStatus; } /** * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see * {@link java.lang.System#currentTimeMillis}. * @return Start of interval. */ public long getStartTimeStamp() { return mBeginTimeStamp; } /** * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see * {@link java.lang.System#currentTimeMillis}. * @return End of interval. */ public long getEndTimeStamp() { return mEndTimeStamp; } /** * Number of bytes received during the bucket's time interval. Statistics are measured at * the network layer, so they include both TCP and UDP usage. * @return Number of bytes. */ public long getRxBytes() { return mRxBytes; } /** * Number of bytes transmitted during the bucket's time interval. Statistics are measured at * the network layer, so they include both TCP and UDP usage. * @return Number of bytes. */ public long getTxBytes() { return mTxBytes; } /** * Number of packets received during the bucket's time interval. Statistics are measured at * the network layer, so they include both TCP and UDP usage. * @return Number of packets. */ public long getRxPackets() { return mRxPackets; } /** * Number of packets transmitted during the bucket's time interval. Statistics are measured * at the network layer, so they include both TCP and UDP usage. * @return Number of packets. */ public long getTxPackets() { return mTxPackets; } } /** * Fills the recycled bucket with data of the next bin in the enumeration. * @param bucketOut Bucket to be filled with data. * @return true if successfully filled the bucket, false otherwise. */ public boolean getNextBucket(Bucket bucketOut) { if (mSummary != null) { return getNextSummaryBucket(bucketOut); } else { return getNextHistoryBucket(bucketOut); } } /** * Check if it is possible to ask for a next bucket in the enumeration. * @return true if there is at least one more bucket. */ public boolean hasNextBucket() { if (mSummary != null) { return mEnumerationIndex < mSummary.size(); } else if (mHistory != null) { return mEnumerationIndex < mHistory.size() || hasNextUid(); } return false; } /** * Closes the enumeration. Call this method before this object gets out of scope. */ @Override public void close() { if (mSession != null) { try { mSession.close(); } catch (RemoteException e) { Log.w(TAG, e); // Otherwise, meh } } mSession = null; if (mCloseGuard != null) { mCloseGuard.close(); } } // -------------------------END OF PUBLIC API----------------------------------- /** * Collects device summary results into a Bucket. * @throws RemoteException */ Bucket getDeviceSummaryForNetwork() throws RemoteException { mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp); // Setting enumeration index beyond end to avoid accidental enumeration over data that does // not belong to the calling user. mEnumerationIndex = mSummary.size(); return getSummaryAggregate(); } /** * Collects summary results and sets summary enumeration mode. * @throws RemoteException */ void startSummaryEnumeration() throws RemoteException { mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp, false /* includeTags */); mEnumerationIndex = 0; } /** * Collects history results for uid and resets history enumeration index. */ void startHistoryEnumeration(int uid, int tag, int state) { mHistory = null; try { mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid, Bucket.convertSet(state), tag, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); setSingleUidTagState(uid, tag, state); } catch (RemoteException e) { Log.w(TAG, e); // Leaving mHistory null } mEnumerationIndex = 0; } /** * Starts uid enumeration for current user. * @throws RemoteException */ void startUserUidEnumeration() throws RemoteException { // TODO: getRelevantUids should be sensitive to time interval. When that's done, // the filtering logic below can be removed. int[] uids = mSession.getRelevantUids(); // Filtering of uids with empty history. IntArray filteredUids = new IntArray(uids.length); for (int uid : uids) { try { NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid, android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); if (history != null && history.size() > 0) { filteredUids.add(uid); } } catch (RemoteException e) { Log.w(TAG, "Error while getting history of uid " + uid, e); } } mUids = filteredUids.toArray(); mUidOrUidIndex = -1; stepHistory(); } /** * Steps to next uid in enumeration and collects history for that. */ private void stepHistory(){ if (hasNextUid()) { stepUid(); mHistory = null; try { mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(), android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); } catch (RemoteException e) { Log.w(TAG, e); // Leaving mHistory null } mEnumerationIndex = 0; } } private void fillBucketFromSummaryEntry(Bucket bucketOut) { bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid); bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag); bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set); bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus( mRecycledSummaryEntry.defaultNetwork); bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered); bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming); bucketOut.mBeginTimeStamp = mStartTimeStamp; bucketOut.mEndTimeStamp = mEndTimeStamp; bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes; bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets; bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes; bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets; } /** * Getting the next item in summary enumeration. * @param bucketOut Next item will be set here. * @return true if a next item could be set. */ private boolean getNextSummaryBucket(Bucket bucketOut) { if (bucketOut != null && mEnumerationIndex < mSummary.size()) { mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry); fillBucketFromSummaryEntry(bucketOut); return true; } return false; } Bucket getSummaryAggregate() { if (mSummary == null) { return null; } Bucket bucket = new Bucket(); if (mRecycledSummaryEntry == null) { mRecycledSummaryEntry = new android.net.NetworkStats.Entry(); } mSummary.getTotal(mRecycledSummaryEntry); fillBucketFromSummaryEntry(bucket); return bucket; } /** * Getting the next item in a history enumeration. * @param bucketOut Next item will be set here. * @return true if a next item could be set. */ private boolean getNextHistoryBucket(Bucket bucketOut) { if (bucketOut != null && mHistory != null) { if (mEnumerationIndex < mHistory.size()) { mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++, mRecycledHistoryEntry); bucketOut.mUid = Bucket.convertUid(getUid()); bucketOut.mTag = Bucket.convertTag(mTag); bucketOut.mState = mState; bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL; bucketOut.mMetered = Bucket.METERED_ALL; bucketOut.mRoaming = Bucket.ROAMING_ALL; bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart; bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart + mRecycledHistoryEntry.bucketDuration; bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes; bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets; bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes; bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets; return true; } else if (hasNextUid()) { stepHistory(); return getNextHistoryBucket(bucketOut); } } return false; } // ------------------ UID LOGIC------------------------ private boolean isUidEnumeration() { return mUids != null; } private boolean hasNextUid() { return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length; } private int getUid() { // Check if uid enumeration. if (isUidEnumeration()) { if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) { throw new IndexOutOfBoundsException( "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length); } return mUids[mUidOrUidIndex]; } // Single uid mode. return mUidOrUidIndex; } private void setSingleUidTagState(int uid, int tag, int state) { mUidOrUidIndex = uid; mTag = tag; mState = state; } private void stepUid() { if (mUids != null) { ++mUidOrUidIndex; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy