/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net; import android.content.Context; import android.net.NetworkInfo.DetailedState; import android.os.Handler; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * This class tracks the data connection associated with Ethernet * This is a singleton class and an instance will be created by * ConnectivityService. * @hide */ public class EthernetDataTracker implements NetworkStateTracker { private static final String NETWORKTYPE = "ETHERNET"; private static final String TAG = "Ethernet"; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); private static boolean mLinkUp; private LinkProperties mLinkProperties; private LinkCapabilities mLinkCapabilities; private NetworkInfo mNetworkInfo; private InterfaceObserver mInterfaceObserver; private String mHwAddr; /* For sending events to connectivity service handler */ private Handler mCsHandler; private Context mContext; private static EthernetDataTracker sInstance; private static String sIfaceMatch = ""; private static String mIface = ""; private INetworkManagementService mNMService; private static class InterfaceObserver extends INetworkManagementEventObserver.Stub { private EthernetDataTracker mTracker; InterfaceObserver(EthernetDataTracker tracker) { super(); mTracker = tracker; } public void interfaceStatusChanged(String iface, boolean up) { Log.d(TAG, "Interface status changed: " + iface + (up ? "up" : "down")); } public void interfaceLinkStateChanged(String iface, boolean up) { if (mIface.equals(iface)) { Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down")); mLinkUp = up; mTracker.mNetworkInfo.setIsAvailable(up); // use DHCP if (up) { mTracker.reconnect(); } else { mTracker.disconnect(); } } } public void interfaceAdded(String iface) { mTracker.interfaceAdded(iface); } public void interfaceRemoved(String iface) { mTracker.interfaceRemoved(iface); } public void limitReached(String limitName, String iface) { // Ignored. } public void interfaceClassDataActivityChanged(String label, boolean active) { // Ignored. } } private EthernetDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, ""); mLinkProperties = new LinkProperties(); mLinkCapabilities = new LinkCapabilities(); } private void interfaceAdded(String iface) { if (!iface.matches(sIfaceMatch)) return; Log.d(TAG, "Adding " + iface); synchronized(this) { if(!mIface.isEmpty()) return; mIface = iface; } // we don't get link status indications unless the iface is up - bring it up try { mNMService.setInterfaceUp(iface); } catch (Exception e) { Log.e(TAG, "Error upping interface " + iface + ": " + e); } mNetworkInfo.setIsAvailable(true); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); } public void disconnect() { NetworkUtils.stopDhcp(mIface); mLinkProperties.clear(); mNetworkInfo.setIsAvailable(false); mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); try { service.clearInterfaceAddresses(mIface); } catch (Exception e) { Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); } } private void interfaceRemoved(String iface) { if (!iface.equals(mIface)) return; Log.d(TAG, "Removing " + iface); disconnect(); mIface = ""; } private void runDhcp() { Thread dhcpThread = new Thread(new Runnable() { public void run() { DhcpResults dhcpResults = new DhcpResults(); if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } mLinkProperties = dhcpResults.linkProperties; mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); } }); dhcpThread.start(); } public static synchronized EthernetDataTracker getInstance() { if (sInstance == null) sInstance = new EthernetDataTracker(); return sInstance; } public Object Clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } public void setTeardownRequested(boolean isRequested) { mTeardownRequested.set(isRequested); } public boolean isTeardownRequested() { return mTeardownRequested.get(); } /** * Begin monitoring connectivity */ public void startMonitoring(Context context, Handler target) { mContext = context; mCsHandler = target; // register for notifications from NetworkManagement Service IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); mNMService = INetworkManagementService.Stub.asInterface(b); mInterfaceObserver = new InterfaceObserver(this); // enable and try to connect to an ethernet interface that // already exists sIfaceMatch = context.getResources().getString( com.android.internal.R.string.config_ethernet_iface_regex); try { final String[] ifaces = mNMService.listInterfaces(); for (String iface : ifaces) { if (iface.matches(sIfaceMatch)) { mIface = iface; mNMService.setInterfaceUp(iface); InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); mLinkUp = config.hasFlag("up"); if (config != null && mHwAddr == null) { mHwAddr = config.getHardwareAddress(); if (mHwAddr != null) { mNetworkInfo.setExtraInfo(mHwAddr); } } // if a DHCP client had previously been started for this interface, then stop it NetworkUtils.stopDhcp(mIface); reconnect(); break; } } } catch (RemoteException e) { Log.e(TAG, "Could not get list of interfaces " + e); } try { mNMService.registerObserver(mInterfaceObserver); } catch (RemoteException e) { Log.e(TAG, "Could not register InterfaceObserver " + e); } } /** * Disable connectivity to a network * TODO: do away with return value after making MobileDataStateTracker async */ public boolean teardown() { mTeardownRequested.set(true); NetworkUtils.stopDhcp(mIface); return true; } /** * Re-enable connectivity to a network after a {@link #teardown()}. */ public boolean reconnect() { if (mLinkUp) { mTeardownRequested.set(false); runDhcp(); } return mLinkUp; } @Override public void captivePortalCheckComplete() { // not implemented } /** * Turn the wireless radio off for a network. * @param turnOn {@code true} to turn the radio on, {@code false} */ public boolean setRadio(boolean turnOn) { return true; } /** * @return true - If are we currently tethered with another device. */ public synchronized boolean isAvailable() { return mNetworkInfo.isAvailable(); } /** * Tells the underlying networking system that the caller wants to * begin using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. * @param feature the name of the feature to be used * @param callingPid the process ID of the process that is issuing this request * @param callingUid the user ID of the process that is issuing this request * @return an integer value representing the outcome of the request. * The interpretation of this value is specific to each networking * implementation+feature combination, except that the value {@code -1} * always indicates failure. * TODO: needs to go away */ public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { return -1; } /** * Tells the underlying networking system that the caller is finished * using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. * @param feature the name of the feature that is no longer needed. * @param callingPid the process ID of the process that is issuing this request * @param callingUid the user ID of the process that is issuing this request * @return an integer value representing the outcome of the request. * The interpretation of this value is specific to each networking * implementation+feature combination, except that the value {@code -1} * always indicates failure. * TODO: needs to go away */ public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { return -1; } @Override public void setUserDataEnable(boolean enabled) { Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); } @Override public void setPolicyDataEnable(boolean enabled) { Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); } /** * Check if private DNS route is set for the network */ public boolean isPrivateDnsRouteSet() { return mPrivateDnsRouteSet.get(); } /** * Set a flag indicating private DNS route is set */ public void privateDnsRouteSet(boolean enabled) { mPrivateDnsRouteSet.set(enabled); } /** * Fetch NetworkInfo for the network */ public synchronized NetworkInfo getNetworkInfo() { return mNetworkInfo; } /** * Fetch LinkProperties for the network */ public synchronized LinkProperties getLinkProperties() { return new LinkProperties(mLinkProperties); } /** * A capability is an Integer/String pair, the capabilities * are defined in the class LinkSocket#Key. * * @return a copy of this connections capabilities, may be empty but never null. */ public LinkCapabilities getLinkCapabilities() { return new LinkCapabilities(mLinkCapabilities); } /** * Fetch default gateway address for the network */ public int getDefaultGatewayAddr() { return mDefaultGatewayAddr.get(); } /** * Check if default route is set */ public boolean isDefaultRouteSet() { return mDefaultRouteSet.get(); } /** * Set a flag indicating default route is set for the network */ public void defaultRouteSet(boolean enabled) { mDefaultRouteSet.set(enabled); } /** * Return the system properties name associated with the tcp buffer sizes * for this network. */ public String getTcpBufferSizesPropName() { return "net.tcp.buffersize.wifi"; } public void setDependencyMet(boolean met) { // not supported on this network } @Override public void addStackedLink(LinkProperties link) { mLinkProperties.addStackedLink(link); } @Override public void removeStackedLink(LinkProperties link) { mLinkProperties.removeStackedLink(link); } @Override public void supplyMessenger(Messenger messenger) { // not supported on this network } }