1 /* 2 * Copyright (C) 2010 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 android.content.Context; 20 import android.net.NetworkInfo.DetailedState; 21 import android.os.Handler; 22 import android.os.IBinder; 23 import android.os.INetworkManagementService; 24 import android.os.Message; 25 import android.os.Messenger; 26 import android.os.RemoteException; 27 import android.os.ServiceManager; 28 import android.util.Log; 29 30 import com.android.server.net.BaseNetworkObserver; 31 32 import java.util.concurrent.atomic.AtomicBoolean; 33 import java.util.concurrent.atomic.AtomicInteger; 34 35 /** 36 * This class tracks the data connection associated with Ethernet 37 * This is a singleton class and an instance will be created by 38 * ConnectivityService. 39 * @hide 40 */ 41 public class EthernetDataTracker extends BaseNetworkStateTracker { 42 private static final String NETWORKTYPE = "ETHERNET"; 43 private static final String TAG = "Ethernet"; 44 45 private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); 46 private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); 47 private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); 48 private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); 49 50 private static boolean mLinkUp; 51 private InterfaceObserver mInterfaceObserver; 52 private String mHwAddr; 53 54 /* For sending events to connectivity service handler */ 55 private Handler mCsHandler; 56 57 private static EthernetDataTracker sInstance; 58 private static String sIfaceMatch = ""; 59 private static String mIface = ""; 60 61 private INetworkManagementService mNMService; 62 63 private static class InterfaceObserver extends BaseNetworkObserver { 64 private EthernetDataTracker mTracker; 65 66 InterfaceObserver(EthernetDataTracker tracker) { 67 super(); 68 mTracker = tracker; 69 } 70 71 @Override 72 public void interfaceStatusChanged(String iface, boolean up) { 73 Log.d(TAG, "Interface status changed: " + iface + (up ? "up" : "down")); 74 } 75 76 @Override 77 public void interfaceLinkStateChanged(String iface, boolean up) { 78 if (mIface.equals(iface)) { 79 Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down")); 80 mLinkUp = up; 81 mTracker.mNetworkInfo.setIsAvailable(up); 82 83 // use DHCP 84 if (up) { 85 mTracker.reconnect(); 86 } else { 87 mTracker.disconnect(); 88 } 89 } 90 } 91 92 @Override 93 public void interfaceAdded(String iface) { 94 mTracker.interfaceAdded(iface); 95 } 96 97 @Override 98 public void interfaceRemoved(String iface) { 99 mTracker.interfaceRemoved(iface); 100 } 101 } 102 103 private EthernetDataTracker() { 104 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, ""); 105 mLinkProperties = new LinkProperties(); 106 mLinkCapabilities = new LinkCapabilities(); 107 } 108 109 private void interfaceAdded(String iface) { 110 if (!iface.matches(sIfaceMatch)) 111 return; 112 113 Log.d(TAG, "Adding " + iface); 114 115 synchronized(this) { 116 if(!mIface.isEmpty()) 117 return; 118 mIface = iface; 119 } 120 121 // we don't get link status indications unless the iface is up - bring it up 122 try { 123 mNMService.setInterfaceUp(iface); 124 } catch (Exception e) { 125 Log.e(TAG, "Error upping interface " + iface + ": " + e); 126 } 127 128 mNetworkInfo.setIsAvailable(true); 129 Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); 130 msg.sendToTarget(); 131 } 132 133 public void disconnect() { 134 135 NetworkUtils.stopDhcp(mIface); 136 137 mLinkProperties.clear(); 138 mNetworkInfo.setIsAvailable(false); 139 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); 140 141 Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); 142 msg.sendToTarget(); 143 144 msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); 145 msg.sendToTarget(); 146 147 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 148 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 149 try { 150 service.clearInterfaceAddresses(mIface); 151 } catch (Exception e) { 152 Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); 153 } 154 } 155 156 private void interfaceRemoved(String iface) { 157 if (!iface.equals(mIface)) 158 return; 159 160 Log.d(TAG, "Removing " + iface); 161 disconnect(); 162 mIface = ""; 163 } 164 165 private void runDhcp() { 166 Thread dhcpThread = new Thread(new Runnable() { 167 public void run() { 168 DhcpResults dhcpResults = new DhcpResults(); 169 if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { 170 Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); 171 return; 172 } 173 mLinkProperties = dhcpResults.linkProperties; 174 175 mNetworkInfo.setIsAvailable(true); 176 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); 177 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); 178 msg.sendToTarget(); 179 } 180 }); 181 dhcpThread.start(); 182 } 183 184 public static synchronized EthernetDataTracker getInstance() { 185 if (sInstance == null) sInstance = new EthernetDataTracker(); 186 return sInstance; 187 } 188 189 public Object Clone() throws CloneNotSupportedException { 190 throw new CloneNotSupportedException(); 191 } 192 193 public void setTeardownRequested(boolean isRequested) { 194 mTeardownRequested.set(isRequested); 195 } 196 197 public boolean isTeardownRequested() { 198 return mTeardownRequested.get(); 199 } 200 201 /** 202 * Begin monitoring connectivity 203 */ 204 public void startMonitoring(Context context, Handler target) { 205 mContext = context; 206 mCsHandler = target; 207 208 // register for notifications from NetworkManagement Service 209 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 210 mNMService = INetworkManagementService.Stub.asInterface(b); 211 212 mInterfaceObserver = new InterfaceObserver(this); 213 214 // enable and try to connect to an ethernet interface that 215 // already exists 216 sIfaceMatch = context.getResources().getString( 217 com.android.internal.R.string.config_ethernet_iface_regex); 218 try { 219 final String[] ifaces = mNMService.listInterfaces(); 220 for (String iface : ifaces) { 221 if (iface.matches(sIfaceMatch)) { 222 mIface = iface; 223 mNMService.setInterfaceUp(iface); 224 InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); 225 mLinkUp = config.hasFlag("up"); 226 if (config != null && mHwAddr == null) { 227 mHwAddr = config.getHardwareAddress(); 228 if (mHwAddr != null) { 229 mNetworkInfo.setExtraInfo(mHwAddr); 230 } 231 } 232 233 // if a DHCP client had previously been started for this interface, then stop it 234 NetworkUtils.stopDhcp(mIface); 235 236 reconnect(); 237 break; 238 } 239 } 240 } catch (RemoteException e) { 241 Log.e(TAG, "Could not get list of interfaces " + e); 242 } 243 244 try { 245 mNMService.registerObserver(mInterfaceObserver); 246 } catch (RemoteException e) { 247 Log.e(TAG, "Could not register InterfaceObserver " + e); 248 } 249 } 250 251 /** 252 * Disable connectivity to a network 253 * TODO: do away with return value after making MobileDataStateTracker async 254 */ 255 public boolean teardown() { 256 mTeardownRequested.set(true); 257 NetworkUtils.stopDhcp(mIface); 258 return true; 259 } 260 261 /** 262 * Re-enable connectivity to a network after a {@link #teardown()}. 263 */ 264 public boolean reconnect() { 265 if (mLinkUp) { 266 mTeardownRequested.set(false); 267 runDhcp(); 268 } 269 return mLinkUp; 270 } 271 272 @Override 273 public void captivePortalCheckComplete() { 274 // not implemented 275 } 276 277 @Override 278 public void captivePortalCheckCompleted(boolean isCaptivePortal) { 279 // not implemented 280 } 281 282 /** 283 * Turn the wireless radio off for a network. 284 * @param turnOn {@code true} to turn the radio on, {@code false} 285 */ 286 public boolean setRadio(boolean turnOn) { 287 return true; 288 } 289 290 /** 291 * @return true - If are we currently tethered with another device. 292 */ 293 public synchronized boolean isAvailable() { 294 return mNetworkInfo.isAvailable(); 295 } 296 297 /** 298 * Tells the underlying networking system that the caller wants to 299 * begin using the named feature. The interpretation of {@code feature} 300 * is completely up to each networking implementation. 301 * @param feature the name of the feature to be used 302 * @param callingPid the process ID of the process that is issuing this request 303 * @param callingUid the user ID of the process that is issuing this request 304 * @return an integer value representing the outcome of the request. 305 * The interpretation of this value is specific to each networking 306 * implementation+feature combination, except that the value {@code -1} 307 * always indicates failure. 308 * TODO: needs to go away 309 */ 310 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { 311 return -1; 312 } 313 314 /** 315 * Tells the underlying networking system that the caller is finished 316 * using the named feature. The interpretation of {@code feature} 317 * is completely up to each networking implementation. 318 * @param feature the name of the feature that is no longer needed. 319 * @param callingPid the process ID of the process that is issuing this request 320 * @param callingUid the user ID of the process that is issuing this request 321 * @return an integer value representing the outcome of the request. 322 * The interpretation of this value is specific to each networking 323 * implementation+feature combination, except that the value {@code -1} 324 * always indicates failure. 325 * TODO: needs to go away 326 */ 327 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { 328 return -1; 329 } 330 331 @Override 332 public void setUserDataEnable(boolean enabled) { 333 Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); 334 } 335 336 @Override 337 public void setPolicyDataEnable(boolean enabled) { 338 Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); 339 } 340 341 /** 342 * Check if private DNS route is set for the network 343 */ 344 public boolean isPrivateDnsRouteSet() { 345 return mPrivateDnsRouteSet.get(); 346 } 347 348 /** 349 * Set a flag indicating private DNS route is set 350 */ 351 public void privateDnsRouteSet(boolean enabled) { 352 mPrivateDnsRouteSet.set(enabled); 353 } 354 355 /** 356 * Fetch NetworkInfo for the network 357 */ 358 public synchronized NetworkInfo getNetworkInfo() { 359 return mNetworkInfo; 360 } 361 362 /** 363 * Fetch LinkProperties for the network 364 */ 365 public synchronized LinkProperties getLinkProperties() { 366 return new LinkProperties(mLinkProperties); 367 } 368 369 /** 370 * A capability is an Integer/String pair, the capabilities 371 * are defined in the class LinkSocket#Key. 372 * 373 * @return a copy of this connections capabilities, may be empty but never null. 374 */ 375 public LinkCapabilities getLinkCapabilities() { 376 return new LinkCapabilities(mLinkCapabilities); 377 } 378 379 /** 380 * Fetch default gateway address for the network 381 */ 382 public int getDefaultGatewayAddr() { 383 return mDefaultGatewayAddr.get(); 384 } 385 386 /** 387 * Check if default route is set 388 */ 389 public boolean isDefaultRouteSet() { 390 return mDefaultRouteSet.get(); 391 } 392 393 /** 394 * Set a flag indicating default route is set for the network 395 */ 396 public void defaultRouteSet(boolean enabled) { 397 mDefaultRouteSet.set(enabled); 398 } 399 400 /** 401 * Return the system properties name associated with the tcp buffer sizes 402 * for this network. 403 */ 404 public String getTcpBufferSizesPropName() { 405 return "net.tcp.buffersize.wifi"; 406 } 407 408 public void setDependencyMet(boolean met) { 409 // not supported on this network 410 } 411 412 @Override 413 public void addStackedLink(LinkProperties link) { 414 mLinkProperties.addStackedLink(link); 415 } 416 417 @Override 418 public void removeStackedLink(LinkProperties link) { 419 mLinkProperties.removeStackedLink(link); 420 } 421 422 @Override 423 public void supplyMessenger(Messenger messenger) { 424 // not supported on this network 425 } 426 } 427