1 /* 2 * Copyright (C) 2008 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 android.net; 18 19 import java.io.FileWriter; 20 import java.io.IOException; 21 22 import android.os.Handler; 23 import android.os.Message; 24 import android.os.SystemProperties; 25 import android.content.Context; 26 import android.text.TextUtils; 27 import android.util.Config; 28 import android.util.Log; 29 30 31 /** 32 * Each subclass of this class keeps track of the state of connectivity 33 * of a network interface. All state information for a network should 34 * be kept in a Tracker class. This superclass manages the 35 * network-type-independent aspects of network state. 36 * 37 * {@hide} 38 */ 39 public abstract class NetworkStateTracker extends Handler { 40 41 protected NetworkInfo mNetworkInfo; 42 protected Context mContext; 43 protected Handler mTarget; 44 protected String mInterfaceName; 45 protected String[] mDnsPropNames; 46 private boolean mPrivateDnsRouteSet; 47 protected int mDefaultGatewayAddr; 48 private boolean mDefaultRouteSet; 49 private boolean mTeardownRequested; 50 51 private static boolean DBG = true; 52 private static final String TAG = "NetworkStateTracker"; 53 54 public static final int EVENT_STATE_CHANGED = 1; 55 public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2; 56 /** 57 * arg1: 1 to show, 0 to hide 58 * arg2: ID of the notification 59 * obj: Notification (if showing) 60 */ 61 public static final int EVENT_NOTIFICATION_CHANGED = 3; 62 public static final int EVENT_CONFIGURATION_CHANGED = 4; 63 public static final int EVENT_ROAMING_CHANGED = 5; 64 public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6; 65 public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7; 66 67 public NetworkStateTracker(Context context, 68 Handler target, 69 int networkType, 70 int subType, 71 String typeName, 72 String subtypeName) { 73 super(); 74 mContext = context; 75 mTarget = target; 76 mTeardownRequested = false; 77 78 this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName); 79 } 80 81 public NetworkInfo getNetworkInfo() { 82 return mNetworkInfo; 83 } 84 85 /** 86 * Return the system properties name associated with the tcp buffer sizes 87 * for this network. 88 */ 89 public abstract String getTcpBufferSizesPropName(); 90 91 /** 92 * Return the IP addresses of the DNS servers available for the mobile data 93 * network interface. 94 * @return a list of DNS addresses, with no holes. 95 */ 96 public String[] getNameServers() { 97 return getNameServerList(mDnsPropNames); 98 } 99 100 /** 101 * Return the IP addresses of the DNS servers available for this 102 * network interface. 103 * @param propertyNames the names of the system properties whose values 104 * give the IP addresses. Properties with no values are skipped. 105 * @return an array of {@code String}s containing the IP addresses 106 * of the DNS servers, in dot-notation. This may have fewer 107 * non-null entries than the list of names passed in, since 108 * some of the passed-in names may have empty values. 109 */ 110 static protected String[] getNameServerList(String[] propertyNames) { 111 String[] dnsAddresses = new String[propertyNames.length]; 112 int i, j; 113 114 for (i = 0, j = 0; i < propertyNames.length; i++) { 115 String value = SystemProperties.get(propertyNames[i]); 116 // The GSM layer sometimes sets a bogus DNS server address of 117 // 0.0.0.0 118 if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "0.0.0.0")) { 119 dnsAddresses[j++] = value; 120 } 121 } 122 return dnsAddresses; 123 } 124 125 public void addPrivateDnsRoutes() { 126 if (DBG) { 127 Log.d(TAG, "addPrivateDnsRoutes for " + this + 128 "(" + mInterfaceName + ") - mPrivateDnsRouteSet = "+mPrivateDnsRouteSet); 129 } 130 if (mInterfaceName != null && !mPrivateDnsRouteSet) { 131 for (String addrString : getNameServers()) { 132 int addr = NetworkUtils.lookupHost(addrString); 133 if (addr != -1 && addr != 0) { 134 if (DBG) Log.d(TAG, " adding "+addrString+" ("+addr+")"); 135 NetworkUtils.addHostRoute(mInterfaceName, addr); 136 } 137 } 138 mPrivateDnsRouteSet = true; 139 } 140 } 141 142 public void removePrivateDnsRoutes() { 143 // TODO - we should do this explicitly but the NetUtils api doesnt 144 // support this yet - must remove all. No worse than before 145 if (mInterfaceName != null && mPrivateDnsRouteSet) { 146 if (DBG) { 147 Log.d(TAG, "removePrivateDnsRoutes for " + mNetworkInfo.getTypeName() + 148 " (" + mInterfaceName + ")"); 149 } 150 NetworkUtils.removeHostRoutes(mInterfaceName); 151 mPrivateDnsRouteSet = false; 152 } 153 } 154 155 public void addDefaultRoute() { 156 if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0) && 157 mDefaultRouteSet == false) { 158 if (DBG) { 159 Log.d(TAG, "addDefaultRoute for " + mNetworkInfo.getTypeName() + 160 " (" + mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr); 161 } 162 NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr); 163 mDefaultRouteSet = true; 164 } 165 } 166 167 public void removeDefaultRoute() { 168 if (mInterfaceName != null && mDefaultRouteSet == true) { 169 if (DBG) { 170 Log.d(TAG, "removeDefaultRoute for " + mNetworkInfo.getTypeName() + " (" + 171 mInterfaceName + ")"); 172 } 173 NetworkUtils.removeDefaultRoute(mInterfaceName); 174 mDefaultRouteSet = false; 175 } 176 } 177 178 /** 179 * Reads the network specific TCP buffer sizes from SystemProperties 180 * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system 181 * wide use 182 */ 183 public void updateNetworkSettings() { 184 String key = getTcpBufferSizesPropName(); 185 String bufferSizes = SystemProperties.get(key); 186 187 if (bufferSizes.length() == 0) { 188 Log.e(TAG, key + " not found in system properties. Using defaults"); 189 190 // Setting to default values so we won't be stuck to previous values 191 key = "net.tcp.buffersize.default"; 192 bufferSizes = SystemProperties.get(key); 193 } 194 195 // Set values in kernel 196 if (bufferSizes.length() != 0) { 197 if (DBG) { 198 Log.v(TAG, "Setting TCP values: [" + bufferSizes 199 + "] which comes from [" + key + "]"); 200 } 201 setBufferSize(bufferSizes); 202 } 203 } 204 205 /** 206 * Release the wakelock, if any, that may be held while handling a 207 * disconnect operation. 208 */ 209 public void releaseWakeLock() { 210 } 211 212 /** 213 * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max] 214 * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem 215 * 216 * @param bufferSizes in the format of "readMin, readInitial, readMax, 217 * writeMin, writeInitial, writeMax" 218 */ 219 private void setBufferSize(String bufferSizes) { 220 try { 221 String[] values = bufferSizes.split(","); 222 223 if (values.length == 6) { 224 final String prefix = "/sys/kernel/ipv4/tcp_"; 225 stringToFile(prefix + "rmem_min", values[0]); 226 stringToFile(prefix + "rmem_def", values[1]); 227 stringToFile(prefix + "rmem_max", values[2]); 228 stringToFile(prefix + "wmem_min", values[3]); 229 stringToFile(prefix + "wmem_def", values[4]); 230 stringToFile(prefix + "wmem_max", values[5]); 231 } else { 232 Log.e(TAG, "Invalid buffersize string: " + bufferSizes); 233 } 234 } catch (IOException e) { 235 Log.e(TAG, "Can't set tcp buffer sizes:" + e); 236 } 237 } 238 239 /** 240 * Writes string to file. Basically same as "echo -n $string > $filename" 241 * 242 * @param filename 243 * @param string 244 * @throws IOException 245 */ 246 private void stringToFile(String filename, String string) throws IOException { 247 FileWriter out = new FileWriter(filename); 248 try { 249 out.write(string); 250 } finally { 251 out.close(); 252 } 253 } 254 255 /** 256 * Record the detailed state of a network, and if it is a 257 * change from the previous state, send a notification to 258 * any listeners. 259 * @param state the new @{code DetailedState} 260 */ 261 public void setDetailedState(NetworkInfo.DetailedState state) { 262 setDetailedState(state, null, null); 263 } 264 265 /** 266 * Record the detailed state of a network, and if it is a 267 * change from the previous state, send a notification to 268 * any listeners. 269 * @param state the new @{code DetailedState} 270 * @param reason a {@code String} indicating a reason for the state change, 271 * if one was supplied. May be {@code null}. 272 * @param extraInfo optional {@code String} providing extra information about the state change 273 */ 274 public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) { 275 if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state); 276 if (state != mNetworkInfo.getDetailedState()) { 277 boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING); 278 String lastReason = mNetworkInfo.getReason(); 279 /* 280 * If a reason was supplied when the CONNECTING state was entered, and no 281 * reason was supplied for entering the CONNECTED state, then retain the 282 * reason that was supplied when going to CONNECTING. 283 */ 284 if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null 285 && lastReason != null) 286 reason = lastReason; 287 mNetworkInfo.setDetailedState(state, reason, extraInfo); 288 Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); 289 msg.sendToTarget(); 290 } 291 } 292 293 protected void setDetailedStateInternal(NetworkInfo.DetailedState state) { 294 mNetworkInfo.setDetailedState(state, null, null); 295 } 296 297 public void setTeardownRequested(boolean isRequested) { 298 mTeardownRequested = isRequested; 299 } 300 301 public boolean isTeardownRequested() { 302 return mTeardownRequested; 303 } 304 305 /** 306 * Send a notification that the results of a scan for network access 307 * points has completed, and results are available. 308 */ 309 protected void sendScanResultsAvailable() { 310 Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo); 311 msg.sendToTarget(); 312 } 313 314 /** 315 * Record the roaming status of the device, and if it is a change from the previous 316 * status, send a notification to any listeners. 317 * @param isRoaming {@code true} if the device is now roaming, {@code false} 318 * if it is no longer roaming. 319 */ 320 protected void setRoamingStatus(boolean isRoaming) { 321 if (isRoaming != mNetworkInfo.isRoaming()) { 322 mNetworkInfo.setRoaming(isRoaming); 323 Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo); 324 msg.sendToTarget(); 325 } 326 } 327 328 protected void setSubtype(int subtype, String subtypeName) { 329 if (mNetworkInfo.isConnected()) { 330 int oldSubtype = mNetworkInfo.getSubtype(); 331 if (subtype != oldSubtype) { 332 mNetworkInfo.setSubtype(subtype, subtypeName); 333 Message msg = mTarget.obtainMessage( 334 EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo); 335 msg.sendToTarget(); 336 } 337 } 338 } 339 340 public abstract void startMonitoring(); 341 342 /** 343 * Disable connectivity to a network 344 * @return {@code true} if a teardown occurred, {@code false} if the 345 * teardown did not occur. 346 */ 347 public abstract boolean teardown(); 348 349 /** 350 * Reenable connectivity to a network after a {@link #teardown()}. 351 */ 352 public abstract boolean reconnect(); 353 354 /** 355 * Turn the wireless radio off for a network. 356 * @param turnOn {@code true} to turn the radio on, {@code false} 357 */ 358 public abstract boolean setRadio(boolean turnOn); 359 360 /** 361 * Returns an indication of whether this network is available for 362 * connections. A value of {@code false} means that some quasi-permanent 363 * condition prevents connectivity to this network. 364 */ 365 public abstract boolean isAvailable(); 366 367 /** 368 * Tells the underlying networking system that the caller wants to 369 * begin using the named feature. The interpretation of {@code feature} 370 * is completely up to each networking implementation. 371 * @param feature the name of the feature to be used 372 * @param callingPid the process ID of the process that is issuing this request 373 * @param callingUid the user ID of the process that is issuing this request 374 * @return an integer value representing the outcome of the request. 375 * The interpretation of this value is specific to each networking 376 * implementation+feature combination, except that the value {@code -1} 377 * always indicates failure. 378 */ 379 public abstract int startUsingNetworkFeature(String feature, int callingPid, int callingUid); 380 381 /** 382 * Tells the underlying networking system that the caller is finished 383 * using the named feature. The interpretation of {@code feature} 384 * is completely up to each networking implementation. 385 * @param feature the name of the feature that is no longer needed. 386 * @param callingPid the process ID of the process that is issuing this request 387 * @param callingUid the user ID of the process that is issuing this request 388 * @return an integer value representing the outcome of the request. 389 * The interpretation of this value is specific to each networking 390 * implementation+feature combination, except that the value {@code -1} 391 * always indicates failure. 392 */ 393 public abstract int stopUsingNetworkFeature(String feature, int callingPid, int callingUid); 394 395 /** 396 * Ensure that a network route exists to deliver traffic to the specified 397 * host via this network interface. 398 * @param hostAddress the IP address of the host to which the route is desired 399 * @return {@code true} on success, {@code false} on failure 400 */ 401 public boolean requestRouteToHost(int hostAddress) { 402 return false; 403 } 404 405 /** 406 * Interprets scan results. This will be called at a safe time for 407 * processing, and from a safe thread. 408 */ 409 public void interpretScanResultsAvailable() { 410 } 411 412 } 413