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