blob: 9f6ab315c30429064b8886cffd28ad68cea7ccfb [file] [log] [blame]
Jesse Wilson8568db52011-06-28 19:06:31 -07001/*
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
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -070019import android.net.NetworkStats;
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070020import android.os.SystemProperties;
21import android.util.Log;
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -070022import android.util.Slog;
23
Jesse Wilson8568db52011-06-28 19:06:31 -070024import dalvik.system.SocketTagger;
Alon Alberteaef3512011-07-19 11:16:09 +030025import libcore.io.IoUtils;
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070026
Jesse Wilson8568db52011-06-28 19:06:31 -070027import java.io.FileDescriptor;
Jesse Wilson8568db52011-06-28 19:06:31 -070028import java.io.FileOutputStream;
29import java.io.IOException;
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070030import java.math.BigInteger;
Jesse Wilson8568db52011-06-28 19:06:31 -070031import java.net.SocketException;
32import java.nio.charset.Charsets;
33
34/**
35 * Assigns tags to sockets for traffic stats.
36 */
37public final class NetworkManagementSocketTagger extends SocketTagger {
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070038 private static final String TAG = "NetworkManagementSocketTagger";
39 private static final boolean LOGD = false;
Jesse Wilson8568db52011-06-28 19:06:31 -070040
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070041 /**
42 * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth
43 * controls have been enabled.
44 */
45 // TODO: remove when always enabled, or once socket tagging silently fails.
46 public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled";
Jesse Wilson8568db52011-06-28 19:06:31 -070047
48 private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070049 @Override
50 protected SocketTags initialValue() {
Jesse Wilson8568db52011-06-28 19:06:31 -070051 return new SocketTags();
52 }
53 };
54
55 public static void install() {
56 SocketTagger.set(new NetworkManagementSocketTagger());
57 }
58
59 public static void setThreadSocketStatsTag(int tag) {
60 threadSocketTags.get().statsTag = tag;
61 }
62
Alon Alberteaef3512011-07-19 11:16:09 +030063 public static int getThreadSocketStatsTag() {
64 return threadSocketTags.get().statsTag;
65 }
66
Jesse Wilson8568db52011-06-28 19:06:31 -070067 public static void setThreadSocketStatsUid(int uid) {
68 threadSocketTags.get().statsUid = uid;
69 }
70
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070071 @Override
72 public void tag(FileDescriptor fd) throws SocketException {
Jesse Wilson8568db52011-06-28 19:06:31 -070073 final SocketTags options = threadSocketTags.get();
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -070074 if (LOGD) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -070075 Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
76 + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
Jesse Wilson8568db52011-06-28 19:06:31 -070077 }
78 try {
79 // TODO: skip tagging when options would be no-op
80 tagSocketFd(fd, options.statsTag, options.statsUid);
81 } catch (IOException e) {
82 throw new SocketException("Problem tagging socket", e);
83 }
84 }
85
86 private void tagSocketFd(FileDescriptor fd, int tag, int uid) throws IOException {
87 final int fdNum = fd.getInt$();
88 if (fdNum == -1 || (tag == -1 && uid == -1)) return;
89
90 String cmd = "t " + fdNum;
91 if (tag == -1) {
92 // Case where just the uid needs adjusting. But probably the caller
93 // will want to track his own name here, just in case.
94 cmd += " 0";
95 } else {
96 cmd += " " + tagToKernel(tag);
97 }
98 if (uid != -1) {
99 cmd += " " + uid;
100 }
101 internalModuleCtrl(cmd);
102 }
103
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -0700104 @Override
105 public void untag(FileDescriptor fd) throws SocketException {
106 if (LOGD) {
107 Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
Jesse Wilson8568db52011-06-28 19:06:31 -0700108 }
109 try {
110 unTagSocketFd(fd);
111 } catch (IOException e) {
112 throw new SocketException("Problem untagging socket", e);
113 }
114 }
115
116 private void unTagSocketFd(FileDescriptor fd) throws IOException {
117 int fdNum = fd.getInt$();
JP Abgrall36bd9842011-08-17 12:06:56 -0700118 final SocketTags options = threadSocketTags.get();
119 if (fdNum == -1 || (options.statsTag == -1 && options.statsUid == -1)) return;
Jesse Wilson8568db52011-06-28 19:06:31 -0700120 String cmd = "u " + fdNum;
121 internalModuleCtrl(cmd);
122 }
123
124 public static class SocketTags {
125 public int statsTag = -1;
126 public int statsUid = -1;
127 }
128
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700129 public static void setKernelCounterSet(int uid, int counterSet) {
130 final StringBuilder command = new StringBuilder();
131 command.append("s ").append(counterSet).append(" ").append(uid);
132 try {
133 internalModuleCtrl(command.toString());
134 } catch (IOException e) {
135 Slog.w(TAG, "problem changing counter set for uid " + uid + " : " + e);
136 }
137 }
138
139 public static void resetKernelUidStats(int uid) {
140 final StringBuilder command = new StringBuilder();
141 command.append("d 0 ").append(uid);
142 try {
143 internalModuleCtrl(command.toString());
144 } catch (IOException e) {
145 Slog.w(TAG, "problem clearing counters for uid " + uid + " : " + e);
146 }
147 }
148
Jesse Wilson8568db52011-06-28 19:06:31 -0700149 /**
150 * Sends commands to the kernel netfilter module.
151 *
152 * @param cmd command string for the qtaguid netfilter module. May not be null.
153 * <p>Supports:
154 * <ul><li>tag a socket:<br>
155 * <code>t <i>sock_fd</i> <i>acct_tag</i> [<i>uid_in_case_caller_is_acting_on_behalf_of</i>]</code><br>
156 * <code>*_tag</code> defaults to default_policy_tag_from_uid(uid_of_caller)<br>
157 * <code>acct_tag</code> is either 0 or greater that 2^32.<br>
158 * <code>uid_*</code> is only settable by privileged UIDs (DownloadManager,...)
159 * </li>
160 * <li>untag a socket, preserving counters:<br>
161 * <code>u <i>sock_fd</i></code>
162 * </li></ul>
163 * <p>Notes:<br>
164 * <ul><li><i>sock_fd</i> is withing the callers process space.</li>
165 * <li><i>*_tag</i> are 64bit values</li></ul>
166 *
167 */
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700168 private static void internalModuleCtrl(String cmd) throws IOException {
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -0700169 if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
Jeff Sharkeyd0d25172011-07-07 10:22:04 -0700170
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -0700171 // TODO: migrate to native library for tagging commands
172 FileOutputStream procOut = null;
Jesse Wilson8568db52011-06-28 19:06:31 -0700173 try {
174 procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl");
Jesse Wilson8568db52011-06-28 19:06:31 -0700175 procOut.write(cmd.getBytes(Charsets.US_ASCII));
176 } finally {
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -0700177 IoUtils.closeQuietly(procOut);
Jesse Wilson8568db52011-06-28 19:06:31 -0700178 }
179 }
180
181 /**
182 * Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned
183 * base-10 format like {@code 2147483647}. Currently strips signed bit to
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -0700184 * avoid using {@link BigInteger}.
Jesse Wilson8568db52011-06-28 19:06:31 -0700185 */
186 public static String tagToKernel(int tag) {
187 // TODO: eventually write in hex, since that's what proc exports
188 // TODO: migrate to direct integer instead of odd shifting
189 return Long.toString((((long) tag) << 32) & 0x7FFFFFFF00000000L);
190 }
191
192 /**
193 * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
194 * format like {@code 0x7fffffff00000000}.
195 */
196 public static int kernelToTag(String string) {
197 // TODO: migrate to direct integer instead of odd shifting
198 return (int) (Long.decode(string) >> 32);
199 }
200}