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