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