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