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