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