Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eedcb952 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

APIs to profile network usage for current UID.

Added startDataProfiling() and stopDataProfiling() to TrafficStats,
which can be used by apps to measure network usage delta between two
points in time.  Currently takes two NetworkStats snapshots and returns
delta, which will eventually include tag-level granularity.  Added
tests for NetworkStats delta subtraction.

Added NMS.getNetworkStatsUidDetail() that returns stats for specific
UID.  Always gives stats access for the calling UID, otherwise enforces
that caller has permission.  Fix readSingleLongFromFile(), since
/proc/ files don't have well-defined lengths.

Change-Id: Ic5b6414d8effbd66846e275b00d4b8a82c74589d
parent 850ae9ac
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -1544,6 +1544,9 @@ public abstract class Context {
     */
     */
    public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
    public static final String NETWORKMANAGEMENT_SERVICE = "network_management";


    /** {@hide} */
    public static final String NETWORK_POLICY_SERVICE = "netpolicy";

    /**
    /**
     * Use with {@link #getSystemService} to retrieve a {@link
     * Use with {@link #getSystemService} to retrieve a {@link
     * android.net.wifi.WifiManager} for handling management of
     * android.net.wifi.WifiManager} for handling management of
+5 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.net;
package android.net;


import android.content.Context;
import android.os.RemoteException;
import android.os.RemoteException;


/**
/**
@@ -43,6 +44,10 @@ public class NetworkPolicyManager {
        mService = service;
        mService = service;
    }
    }


    public static NetworkPolicyManager getSystemService(Context context) {
        return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
    }

    /**
    /**
     * Set policy flags for specific UID.
     * Set policy flags for specific UID.
     *
     *
+47 −7
Original line number Original line Diff line number Diff line
@@ -36,6 +36,9 @@ public class NetworkStats implements Parcelable {
    /** {@link #uid} value when entry is summarized over all UIDs. */
    /** {@link #uid} value when entry is summarized over all UIDs. */
    public static final int UID_ALL = 0;
    public static final int UID_ALL = 0;


    // NOTE: data should only be accounted for once in this structure; if data
    // is broken out, the summarized version should not be included.

    /**
    /**
     * {@link SystemClock#elapsedRealtime()} timestamp when this data was
     * {@link SystemClock#elapsedRealtime()} timestamp when this data was
     * generated.
     * generated.
@@ -81,12 +84,13 @@ public class NetworkStats implements Parcelable {
            mTx = new long[size];
            mTx = new long[size];
        }
        }


        public void addEntry(String iface, int uid, long rx, long tx) {
        public Builder addEntry(String iface, int uid, long rx, long tx) {
            mIface[mIndex] = iface;
            mIface[mIndex] = iface;
            mUid[mIndex] = uid;
            mUid[mIndex] = uid;
            mRx[mIndex] = rx;
            mRx[mIndex] = rx;
            mTx[mIndex] = tx;
            mTx[mIndex] = tx;
            mIndex++;
            mIndex++;
            return this;
        }
        }


        public NetworkStats build() {
        public NetworkStats build() {
@@ -97,11 +101,17 @@ public class NetworkStats implements Parcelable {
        }
        }
    }
    }


    public int length() {
        // length is identical for all fields
        return iface.length;
    }

    /**
    /**
     * Find first stats index that matches the requested parameters.
     * Find first stats index that matches the requested parameters.
     */
     */
    public int findIndex(String iface, int uid) {
    public int findIndex(String iface, int uid) {
        for (int i = 0; i < this.iface.length; i++) {
        final int length = length();
        for (int i = 0; i < length; i++) {
            if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
            if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
                return i;
                return i;
            }
            }
@@ -109,13 +119,38 @@ public class NetworkStats implements Parcelable {
        return -1;
        return -1;
    }
    }


    private static boolean equal(Object a, Object b) {
    /**
        return a == b || (a != null && a.equals(b));
     * Subtract the given {@link NetworkStats}, effectively leaving the delta
     * between two snapshots in time. Assumes that statistics rows collect over
     * time, and that none of them have disappeared.
     */
    public NetworkStats subtract(NetworkStats value) {
        // result will have our rows, but no meaningful timestamp
        final int length = length();
        final NetworkStats.Builder result = new NetworkStats.Builder(-1, length);

        for (int i = 0; i < length; i++) {
            final String iface = this.iface[i];
            final int uid = this.uid[i];

            // find remote row that matches, and subtract
            final int j = value.findIndex(iface, uid);
            if (j == -1) {
                // newly appearing row, return entire value
                result.addEntry(iface, uid, this.rx[i], this.tx[i]);
            } else {
                // existing row, subtract remote value
                final long rx = this.rx[i] - value.rx[j];
                final long tx = this.tx[i] - value.tx[j];
                result.addEntry(iface, uid, rx, tx);
            }
        }
        }


    /** {@inheritDoc} */
        return result.build();
    public int describeContents() {
    }
        return 0;

    private static boolean equal(Object a, Object b) {
        return a == b || (a != null && a.equals(b));
    }
    }


    public void dump(String prefix, PrintWriter pw) {
    public void dump(String prefix, PrintWriter pw) {
@@ -137,6 +172,11 @@ public class NetworkStats implements Parcelable {
        return writer.toString();
        return writer.toString();
    }
    }


    /** {@inheritDoc} */
    public int describeContents() {
        return 0;
    }

    /** {@inheritDoc} */
    /** {@inheritDoc} */
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(elapsedRealtime);
        dest.writeLong(elapsedRealtime);
+72 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,12 @@


package android.net;
package android.net;


import android.content.Context;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceManager;

import dalvik.system.BlockGuard;
import dalvik.system.BlockGuard;


import java.net.Socket;
import java.net.Socket;
@@ -35,6 +41,17 @@ public class TrafficStats {
     */
     */
    public final static int UNSUPPORTED = -1;
    public final static int UNSUPPORTED = -1;


    /**
     * Snapshot of {@link NetworkStats} when the currently active profiling
     * session started, or {@code null} if no session active.
     *
     * @see #startDataProfiling(Context)
     * @see #stopDataProfiling(Context)
     */
    private static NetworkStats sActiveProfilingStart;

    private static Object sProfilingLock = new Object();

    /**
    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. Only one active tag per thread is supported.
     * from the current thread. Only one active tag per thread is supported.
@@ -92,6 +109,44 @@ public class TrafficStats {
        BlockGuard.untagSocketFd(socket.getFileDescriptor$());
        BlockGuard.untagSocketFd(socket.getFileDescriptor$());
    }
    }


    /**
     * Start profiling data usage for current UID. Only one profiling session
     * can be active at a time.
     *
     * @hide
     */
    public static void startDataProfiling(Context context) {
        synchronized (sProfilingLock) {
            if (sActiveProfilingStart != null) {
                throw new IllegalStateException("already profiling data");
            }

            // take snapshot in time; we calculate delta later
            sActiveProfilingStart = getNetworkStatsForUid(context);
        }
    }

    /**
     * Stop profiling data usage for current UID.
     *
     * @return Detailed {@link NetworkStats} of data that occurred since last
     *         {@link #startDataProfiling(Context)} call.
     * @hide
     */
    public static NetworkStats stopDataProfiling(Context context) {
        synchronized (sProfilingLock) {
            if (sActiveProfilingStart == null) {
                throw new IllegalStateException("not profiling data");
            }

            // subtract starting values and return delta
            final NetworkStats profilingStop = getNetworkStatsForUid(context);
            final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
            sActiveProfilingStart = null;
            return profilingDelta;
        }
    }

    /**
    /**
     * Get the total number of packets transmitted through the mobile interface.
     * Get the total number of packets transmitted through the mobile interface.
     *
     *
@@ -350,4 +405,21 @@ public class TrafficStats {
     * {@link #UNSUPPORTED} will be returned.
     * {@link #UNSUPPORTED} will be returned.
     */
     */
    public static native long getUidUdpRxPackets(int uid);
    public static native long getUidUdpRxPackets(int uid);

    /**
     * Return detailed {@link NetworkStats} for the current UID. Requires no
     * special permission.
     */
    private static NetworkStats getNetworkStatsForUid(Context context) {
        final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
        final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
                binder);

        final int uid = android.os.Process.myUid();
        try {
            return service.getNetworkStatsUidDetail(uid);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }
}
}
+6 −0
Original line number Original line Diff line number Diff line
@@ -213,6 +213,12 @@ interface INetworkManagementService
     */
     */
    NetworkStats getNetworkStatsDetail();
    NetworkStats getNetworkStatsDetail();


    /**
     * Return detailed network statistics for the requested UID,
     * including interface and tag details.
     */
    NetworkStats getNetworkStatsUidDetail(int uid);

    /**
    /**
     * Configures bandwidth throttling on an interface.
     * Configures bandwidth throttling on an interface.
     */
     */
Loading