1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.os.SystemProperties;
20import android.util.Log;
21import android.util.Slog;
22
23import dalvik.system.SocketTagger;
24
25import java.io.FileDescriptor;
26import java.net.SocketException;
27
28/**
29 * Assigns tags to sockets for traffic stats.
30 */
31public final class NetworkManagementSocketTagger extends SocketTagger {
32    private static final String TAG = "NetworkManagementSocketTagger";
33    private static final boolean LOGD = false;
34
35    /**
36     * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth
37     * controls have been enabled.
38     */
39    // TODO: remove when always enabled, or once socket tagging silently fails.
40    public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled";
41
42    private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
43        @Override
44        protected SocketTags initialValue() {
45            return new SocketTags();
46        }
47    };
48
49    public static void install() {
50        SocketTagger.set(new NetworkManagementSocketTagger());
51    }
52
53    public static void setThreadSocketStatsTag(int tag) {
54        threadSocketTags.get().statsTag = tag;
55    }
56
57    public static int getThreadSocketStatsTag() {
58        return threadSocketTags.get().statsTag;
59    }
60
61    public static void setThreadSocketStatsUid(int uid) {
62        threadSocketTags.get().statsUid = uid;
63    }
64
65    @Override
66    public void tag(FileDescriptor fd) throws SocketException {
67        final SocketTags options = threadSocketTags.get();
68        if (LOGD) {
69            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
70                    + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
71        }
72        // TODO: skip tagging when options would be no-op
73        tagSocketFd(fd, options.statsTag, options.statsUid);
74    }
75
76    private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
77        if (tag == -1 && uid == -1) return;
78
79        if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
80            final int errno = native_tagSocketFd(fd, tag, uid);
81            if (errno < 0) {
82                Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
83                      + tag + ", " +
84                      + uid + ") failed with errno" + errno);
85            }
86        }
87    }
88
89    @Override
90    public void untag(FileDescriptor fd) throws SocketException {
91        if (LOGD) {
92            Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
93        }
94        unTagSocketFd(fd);
95    }
96
97    private void unTagSocketFd(FileDescriptor fd) {
98        final SocketTags options = threadSocketTags.get();
99        if (options.statsTag == -1 && options.statsUid == -1) return;
100
101        if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
102            final int errno = native_untagSocketFd(fd);
103            if (errno < 0) {
104                Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
105            }
106        }
107    }
108
109    public static class SocketTags {
110        public int statsTag = -1;
111        public int statsUid = -1;
112    }
113
114    public static void setKernelCounterSet(int uid, int counterSet) {
115        if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
116            final int errno = native_setCounterSet(counterSet, uid);
117            if (errno < 0) {
118                Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
119                        + errno);
120            }
121        }
122    }
123
124    public static void resetKernelUidStats(int uid) {
125        if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
126            int errno = native_deleteTagData(0, uid);
127            if (errno < 0) {
128                Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
129            }
130        }
131    }
132
133    /**
134     * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
135     * format like {@code 0x7fffffff00000000}.
136     */
137    public static int kernelToTag(String string) {
138        int length = string.length();
139        if (length > 10) {
140            return Long.decode(string.substring(0, length - 8)).intValue();
141        } else {
142            return 0;
143        }
144    }
145
146    private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
147    private static native int native_untagSocketFd(FileDescriptor fd);
148    private static native int native_setCounterSet(int uid, int counterSetNum);
149    private static native int native_deleteTagData(int tag, int uid);
150}
151