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

Commit 8994730f authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Better network stats parsing, integer tags, async."

parents eb8283d4 4414cea1
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -11240,7 +11240,8 @@ package android.net {
    method public static long getUidUdpRxPackets(int);
    method public static long getUidUdpTxBytes(int);
    method public static long getUidUdpTxPackets(int);
    method public static void setThreadStatsTag(java.lang.String);
    method public static void setThreadStatsTag(int);
    method public static deprecated void setThreadStatsTag(java.lang.String);
    method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
    method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
    field public static final int UNSUPPORTED = -1; // 0xffffffff
+36 −2
Original line number Diff line number Diff line
@@ -16,14 +16,21 @@

package android.net;

import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.text.format.Time.MONTH_DAY;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.RemoteException;
import android.text.format.Time;

import com.google.android.collect.Sets;

import java.io.PrintWriter;
import java.util.HashSet;

/**
 * Manager for creating and modifying network policy rules.
@@ -210,8 +217,35 @@ public class NetworkPolicyManager {
     * usually to protect critical system services.
     */
    public static boolean isUidValidForPolicy(Context context, int uid) {
        return (uid >= android.os.Process.FIRST_APPLICATION_UID
                && uid <= android.os.Process.LAST_APPLICATION_UID);
        // first, quick-reject non-applications
        if (uid < android.os.Process.FIRST_APPLICATION_UID
                || uid > android.os.Process.LAST_APPLICATION_UID) {
            return false;
        }

        final PackageManager pm = context.getPackageManager();
        final HashSet<Signature> systemSignature;
        try {
            systemSignature = Sets.newHashSet(
                    pm.getPackageInfo("android", GET_SIGNATURES).signatures);
        } catch (NameNotFoundException e) {
            throw new RuntimeException("problem finding system signature", e);
        }

        try {
            // reject apps signed with system cert
            for (String packageName : pm.getPackagesForUid(uid)) {
                final HashSet<Signature> packageSignature = Sets.newHashSet(
                        pm.getPackageInfo(packageName, GET_SIGNATURES).signatures);
                if (packageSignature.containsAll(systemSignature)) {
                    return false;
                }
            }
        } catch (NameNotFoundException e) {
        }

        // nothing found above; we can apply policy to UID
        return true;
    }

    /** {@hide} */
+35 −3
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package android.net;

import android.app.DownloadManager;
import android.app.backup.BackupManager;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -49,6 +52,27 @@ public class TrafficStats {
     */
    public static final int UID_REMOVED = -4;

    /**
     * Default tag value for {@link DownloadManager} traffic.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFF0001;

    /**
     * Default tag value for {@link MediaPlayer} traffic.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_MEDIA = 0xFFFF0002;

    /**
     * Default tag value for {@link BackupManager} traffic.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_BACKUP = 0xFFFF0003;

    /**
     * Snapshot of {@link NetworkStats} when the currently active profiling
     * session started, or {@code null} if no session active.
@@ -67,12 +91,20 @@ public class TrafficStats {
     * Changes only take effect during subsequent calls to
     * {@link #tagSocket(Socket)}.
     */
    public static void setThreadStatsTag(String tag) {
    public static void setThreadStatsTag(int tag) {
        BlockGuard.setThreadSocketStatsTag(tag);
    }

    /**
     * @deprecated unsupported, will eventually be removed
     */
    @Deprecated
    public static void setThreadStatsTag(String tag) {
        setThreadStatsTag(tag.hashCode());
    }

    public static void clearThreadStatsTag() {
        BlockGuard.setThreadSocketStatsTag(null);
        BlockGuard.setThreadSocketStatsTag(-1);
    }

    /**
@@ -103,7 +135,7 @@ public class TrafficStats {
     * parameters. When finished, call {@link #untagSocket(Socket)} to remove
     * statistics parameters.
     *
     * @see #setThreadStatsTag(String)
     * @see #setThreadStatsTag(int)
     * @see #setThreadStatsUid(int)
     */
    public static void tagSocket(Socket socket) throws SocketException {
+75 −24
Original line number Diff line number Diff line
@@ -38,6 +38,11 @@ import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;

import com.google.android.collect.Lists;
import com.google.android.collect.Maps;

import dalvik.system.BlockGuard;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
@@ -48,6 +53,7 @@ import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
@@ -65,9 +71,18 @@ class NetworkManagementService extends INetworkManagementService.Stub {
    private static final int ADD = 1;
    private static final int REMOVE = 2;

    /** Path to {@code /proc/uid_stat}. */
    @Deprecated
    private static final File STATS_UIDSTAT = new File("/proc/uid_stat");
    private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats");
    private final File mProcStatsUidstat;
    /** Path to {@code /proc/net/xt_qtaguid/stats}. */
    private final File mProcStatsNetfilter;

    /** {@link #mProcStatsNetfilter} headers. */
    private static final String KEY_IFACE = "iface";
    private static final String KEY_TAG_HEX = "acct_tag_hex";
    private static final String KEY_UID = "uid_tag_int";
    private static final String KEY_RX = "rx_bytes";
    private static final String KEY_TX = "tx_bytes";

    class NetdResponseCode {
        public static final int InterfaceListResult       = 110;
@@ -107,10 +122,13 @@ class NetworkManagementService extends INetworkManagementService.Stub {
     *
     * @param context  Binder context for this service
     */
    private NetworkManagementService(Context context) {
    private NetworkManagementService(Context context, File procRoot) {
        mContext = context;
        mObservers = new ArrayList<INetworkManagementEventObserver>();

        mProcStatsUidstat = new File(procRoot, "uid_stat");
        mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");

        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
            return;
        }
@@ -121,7 +139,8 @@ class NetworkManagementService extends INetworkManagementService.Stub {
    }

    public static NetworkManagementService create(Context context) throws InterruptedException {
        NetworkManagementService service = new NetworkManagementService(context);
        NetworkManagementService service = new NetworkManagementService(
                context, new File("/proc/"));
        if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
        service.mThread.start();
        if (DBG) Slog.d(TAG, "Awaiting socket connection");
@@ -130,6 +149,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
        return service;
    }

    // @VisibleForTesting
    public static NetworkManagementService createForTest(Context context, File procRoot) {
        // TODO: eventually connect with mock netd
        return new NetworkManagementService(context, procRoot);
    }

    public void registerObserver(INetworkManagementEventObserver obs) {
        Slog.d(TAG, "Registering observer");
        mObservers.add(obs);
@@ -888,7 +913,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");

        if (STATS_NETFILTER.exists()) {
        if (mProcStatsNetfilter.exists()) {
            return getNetworkStatsDetailNetfilter(UID_ALL);
        } else {
            return getNetworkStatsDetailUidstat(UID_ALL);
@@ -902,7 +927,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
                    android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
        }

        if (STATS_NETFILTER.exists()) {
        if (mProcStatsNetfilter.exists()) {
            return getNetworkStatsDetailNetfilter(uid);
        } else {
            return getNetworkStatsDetailUidstat(uid);
@@ -914,35 +939,35 @@ class NetworkManagementService extends INetworkManagementService.Stub {
     */
    private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
        final ArrayList<String> keys = Lists.newArrayList();
        final ArrayList<String> values = Lists.newArrayList();
        final HashMap<String, String> parsed = Maps.newHashMap();

        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(STATS_NETFILTER));
            reader = new BufferedReader(new FileReader(mProcStatsNetfilter));

            // assumes format from kernel:
            // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes

            // skip first line, which is legend
            // parse first line as header
            String line = reader.readLine();
            while ((line = reader.readLine()) != null) {
                final StringTokenizer t = new StringTokenizer(line);
            splitLine(line, keys);

                final String idx = t.nextToken();
                final String iface = t.nextToken();
            // parse remaining lines
            while ((line = reader.readLine()) != null) {
                splitLine(line, values);
                parseLine(keys, values, parsed);

                try {
                    // TODO: kernel currently emits tag in upper half of long;
                    // eventually switch to directly using int.
                    final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32);
                    final int uid = Integer.parseInt(t.nextToken());
                    final long rx = Long.parseLong(t.nextToken());
                    final long tx = Long.parseLong(t.nextToken());
                    final String iface = parsed.get(KEY_IFACE);
                    final int tag = BlockGuard.kernelToTag(parsed.get(KEY_TAG_HEX));
                    final int uid = Integer.parseInt(parsed.get(KEY_UID));
                    final long rx = Long.parseLong(parsed.get(KEY_RX));
                    final long tx = Long.parseLong(parsed.get(KEY_TX));

                    if (limitUid == UID_ALL || limitUid == uid) {
                        stats.addEntry(iface, uid, tag, rx, tx);
                    }
                } catch (NumberFormatException e) {
                    Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e);
                    Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
                }
            }
        } catch (IOException e) {
@@ -964,7 +989,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
    private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
        final String[] knownUids;
        if (limitUid == UID_ALL) {
            knownUids = STATS_UIDSTAT.list();
            knownUids = mProcStatsUidstat.list();
        } else {
            knownUids = new String[] { String.valueOf(limitUid) };
        }
@@ -973,7 +998,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
                SystemClock.elapsedRealtime(), knownUids.length);
        for (String uid : knownUids) {
            final int uidInt = Integer.parseInt(uid);
            final File uidPath = new File(STATS_UIDSTAT, uid);
            final File uidPath = new File(mProcStatsUidstat, uid);
            final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
            final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
            stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
@@ -1047,6 +1072,32 @@ class NetworkManagementService extends INetworkManagementService.Stub {
        mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable")));
    }

    /**
     * Split given line into {@link ArrayList}.
     */
    private static void splitLine(String line, ArrayList<String> outSplit) {
        outSplit.clear();

        final StringTokenizer t = new StringTokenizer(line);
        while (t.hasMoreTokens()) {
            outSplit.add(t.nextToken());
        }
    }

    /**
     * Zip the two given {@link ArrayList} as key and value pairs into
     * {@link HashMap}.
     */
    private static void parseLine(
            ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
        outParsed.clear();

        final int size = Math.min(keys.size(), values.size());
        for (int i = 0; i < size; i++) {
            outParsed.put(keys.get(i), values.get(i));
        }
    }

    /**
     * Utility method to read a single plain-text {@link Long} from the given
     * {@link File}, usually from a {@code /proc/} filesystem.
+48 −58
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IPowerManager;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
@@ -148,6 +149,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

    private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;

    private static final int MSG_RULES_CHANGED = 0x1;
    private static final int MSG_METERED_IFACES_CHANGED = 0x2;

    private final Context mContext;
    private final IActivityManager mActivityManager;
    private final IPowerManager mPowerManager;
@@ -210,7 +214,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

        mHandlerThread = new HandlerThread(TAG);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);

        mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
    }
@@ -269,9 +273,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            // only someone like AMS should only be calling us
            mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);

            // skip when UID couldn't have any policy
            if (!isUidValidForPolicy(mContext, uid)) return;

            synchronized (mRulesLock) {
                // because a uid can have multiple pids running inside, we need to
                // remember all pid states and summarize foreground at uid level.
@@ -292,9 +293,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            // only someone like AMS should only be calling us
            mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);

            // skip when UID couldn't have any policy
            if (!isUidValidForPolicy(mContext, uid)) return;

            synchronized (mRulesLock) {
                // clear records and recompute, when they exist
                final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
@@ -599,20 +597,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            }
        }

        // dispatch changed rule to existing listeners
        // TODO: dispatch outside of holding lock
        final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
        final int length = mListeners.beginBroadcast();
        for (int i = 0; i < length; i++) {
            final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
            if (listener != null) {
                try {
                    listener.onMeteredIfacesChanged(meteredIfaces);
                } catch (RemoteException e) {
                }
            }
        }
        mListeners.finishBroadcast();
        mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
    }

    /**
@@ -804,32 +790,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

        mListeners.register(listener);

        synchronized (mRulesLock) {
            // dispatch any existing rules to new listeners
            // TODO: dispatch outside of holding lock
            final int size = mUidRules.size();
            for (int i = 0; i < size; i++) {
                final int uid = mUidRules.keyAt(i);
                final int uidRules = mUidRules.valueAt(i);
                if (uidRules != RULE_ALLOW_ALL) {
                    try {
                        listener.onUidRulesChanged(uid, uidRules);
                    } catch (RemoteException e) {
                    }
                }
            }

            // dispatch any metered ifaces to new listeners
            // TODO: dispatch outside of holding lock
            if (mMeteredIfaces.size() > 0) {
                final String[] meteredIfaces = mMeteredIfaces.toArray(
                        new String[mMeteredIfaces.size()]);
                try {
                    listener.onMeteredIfacesChanged(meteredIfaces);
                } catch (RemoteException e) {
                }
            }
        }
        // TODO: consider dispatching existing rules to new listeners
    }

    @Override
@@ -978,8 +939,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    }

    private void updateRulesForUidLocked(int uid) {
        if (!isUidValidForPolicy(mContext, uid)) return;

        final int uidPolicy = getUidPolicy(uid);
        final boolean uidForeground = isUidForeground(uid);

@@ -999,7 +958,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        //kernelSetUidRejectPaid(uid, rejectPaid);

        // dispatch changed rule to existing listeners
        // TODO: dispatch outside of holding lock
        mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget();
    }

    private Handler.Callback mHandlerCallback = new Handler.Callback() {
        /** {@inheritDoc} */
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RULES_CHANGED: {
                    final int uid = msg.arg1;
                    final int uidRules = msg.arg2;
                    final int length = mListeners.beginBroadcast();
                    for (int i = 0; i < length; i++) {
                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
@@ -1011,7 +979,29 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                        }
                    }
                    mListeners.finishBroadcast();
                    return true;
                }
                case MSG_METERED_IFACES_CHANGED: {
                    final String[] meteredIfaces = (String[]) msg.obj;
                    final int length = mListeners.beginBroadcast();
                    for (int i = 0; i < length; i++) {
                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
                        if (listener != null) {
                            try {
                                listener.onMeteredIfacesChanged(meteredIfaces);
                            } catch (RemoteException e) {
                            }
                        }
                    }
                    mListeners.finishBroadcast();
                    return true;
                }
                default: {
                    return false;
                }
            }
        }
    };

    private String getActiveSubscriberId() {
        final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
Loading