From 051a664b143e9ed7ecbef91911d0fe6c34bb4e14 Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Wed, 13 Jul 2011 13:44:13 -0700 Subject: [PATCH] Remove STOPSHIP but allow seamless Handoff when possible. If an address is removed we must reset the connection but only for the connections associated with that address. For now we're doing the "all" addresses for a type (IPv6 or IPv4) in the future we only need to reset a particular addresses connections. Bug: 4981919 Change-Id: I97f8071a3ed6f827ed22f32216ca5011bfe6c1d9 --- core/java/android/net/LinkProperties.java | 137 ++++++++++++++---- .../android/server/ConnectivityService.java | 59 +++++++- 2 files changed, 164 insertions(+), 32 deletions(-) diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 19894a039a..f2f0e825c0 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -52,11 +52,26 @@ import java.util.Collections; public class LinkProperties implements Parcelable { String mIfaceName; - private Collection mLinkAddresses; - private Collection mDnses; - private Collection mRoutes; + private Collection mLinkAddresses = new ArrayList(); + private Collection mDnses = new ArrayList(); + private Collection mRoutes = new ArrayList(); private ProxyProperties mHttpProxy; + public static class CompareAddressesResult { + public ArrayList removed = new ArrayList(); + public ArrayList added = new ArrayList(); + + @Override + public String toString() { + String retVal = "removedAddresses=["; + for (LinkAddress addr : removed) retVal += addr.toString() + ","; + retVal += "] addedAddresses=["; + for (LinkAddress addr : added) retVal += addr.toString() + ","; + retVal += "]"; + return retVal; + } + } + public LinkProperties() { clear(); } @@ -121,9 +136,9 @@ public class LinkProperties implements Parcelable { public void clear() { mIfaceName = null; - mLinkAddresses = new ArrayList(); - mDnses = new ArrayList(); - mRoutes = new ArrayList(); + mLinkAddresses.clear(); + mDnses.clear(); + mRoutes.clear(); mHttpProxy = null; } @@ -155,6 +170,63 @@ public class LinkProperties implements Parcelable { return ifaceName + linkAddresses + routes + dns + proxy; } + /** + * Compares this {@code LinkProperties} interface name against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalInterfaceName(LinkProperties target) { + return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); + } + + /** + * Compares this {@code LinkProperties} interface name against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalAddresses(LinkProperties target) { + Collection targetAddresses = target.getAddresses(); + Collection sourceAddresses = getAddresses(); + return (sourceAddresses.size() == targetAddresses.size()) ? + sourceAddresses.containsAll(targetAddresses) : false; + } + + /** + * Compares this {@code LinkProperties} DNS addresses against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalDnses(LinkProperties target) { + Collection targetDnses = target.getDnses(); + return (mDnses.size() == targetDnses.size()) ? + mDnses.containsAll(targetDnses) : false; + } + + /** + * Compares this {@code LinkProperties} Routes against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalRoutes(LinkProperties target) { + Collection targetRoutes = target.getRoutes(); + return (mRoutes.size() == targetRoutes.size()) ? + mRoutes.containsAll(targetRoutes) : false; + } + + /** + * Compares this {@code LinkProperties} HttpProxy against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalHttpProxy(LinkProperties target) { + return getHttpProxy() == null ? target.getHttpProxy() == null : + getHttpProxy().equals(target.getHttpProxy()); + } @Override /** @@ -176,30 +248,41 @@ public class LinkProperties implements Parcelable { if (!(obj instanceof LinkProperties)) return false; - boolean sameAddresses; - boolean sameDnses; - boolean sameRoutes; - LinkProperties target = (LinkProperties) obj; - Collection targetAddresses = target.getAddresses(); - Collection sourceAddresses = getAddresses(); - sameAddresses = (sourceAddresses.size() == targetAddresses.size()) ? - sourceAddresses.containsAll(targetAddresses) : false; + return isIdenticalInterfaceName(target) && + isIdenticalAddresses(target) && + isIdenticalDnses(target) && + isIdenticalRoutes(target) && + isIdenticalHttpProxy(target); + } - Collection targetDnses = target.getDnses(); - sameDnses = (mDnses.size() == targetDnses.size()) ? - mDnses.containsAll(targetDnses) : false; - - Collection targetRoutes = target.getRoutes(); - sameRoutes = (mRoutes.size() == targetRoutes.size()) ? - mRoutes.containsAll(targetRoutes) : false; - - return - sameAddresses && sameDnses && sameRoutes - && TextUtils.equals(getInterfaceName(), target.getInterfaceName()) - && (getHttpProxy() == null ? target.getHttpProxy() == null : - getHttpProxy().equals(target.getHttpProxy())); + /** + * Return two lists, a list of addresses that would be removed from + * mLinkAddresses and a list of addresses that would be added to + * mLinkAddress which would then result in target and mLinkAddresses + * being the same list. + * + * @param target is a new list of addresses + * @return the removed and added lists. + */ + public CompareAddressesResult compareAddresses(LinkProperties target) { + /* + * Duplicate the LinkAddresses into removed, we will be removing + * address which are common between mLinkAddresses and target + * leaving the addresses that are different. And address which + * are in target but not in mLinkAddresses are placed in the + * addedAddresses. + */ + CompareAddressesResult result = new CompareAddressesResult(); + result.removed = new ArrayList(mLinkAddresses); + result.added.clear(); + for (LinkAddress newAddress : target.getLinkAddresses()) { + if (! result.removed.remove(newAddress)) { + result.added.add(newAddress); + } + } + return result; } @Override diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 42be7565e0..812c1eb1d5 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -28,6 +28,7 @@ import android.net.EthernetDataTracker; import android.net.IConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.LinkProperties.CompareAddressesResult; import android.net.MobileDataStateTracker; import android.net.NetworkConfig; import android.net.NetworkInfo; @@ -61,6 +62,7 @@ import java.io.FileDescriptor; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.Inet4Address; import java.net.UnknownHostException; @@ -76,6 +78,7 @@ import java.util.List; public class ConnectivityService extends IConnectivityManager.Stub { private static final boolean DBG = true; + private static final boolean VDBG = true; private static final String TAG = "ConnectivityService"; // how long to wait before switching back to a radio's default network @@ -98,6 +101,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private NetworkStateTracker mNetTrackers[]; + /** + * The link properties that define the current links + */ + private LinkProperties mCurrentLinkProperties[]; + /** * A per Net list of the PID's that requested access to the net * used both as a refcount and for per-PID DNS selection @@ -302,6 +310,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; + mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1]; mNetworkPreference = getPersistedNetworkPreference(); @@ -442,6 +451,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetConfigs[netType].radio); continue; } + mCurrentLinkProperties[netType] = mNetTrackers[netType].getLinkProperties(); } mTethering = new Tethering(mContext, mHandler.getLooper()); @@ -1409,6 +1419,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { * right routing table entries exist. */ private void handleConnectivityChange(int netType, boolean doReset) { + int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0; + /* * If a non-default network is enabled, add the host routes that * will allow it's DNS servers to be accessed. @@ -1416,6 +1428,45 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleDnsConfigurationChange(netType); if (mNetTrackers[netType].getNetworkInfo().isConnected()) { + LinkProperties newLp = mNetTrackers[netType].getLinkProperties(); + LinkProperties curLp = mCurrentLinkProperties[netType]; + mCurrentLinkProperties[netType] = newLp; + if (VDBG) { + log("handleConnectivityChange: changed linkProperty[" + netType + "]:" + + " doReset=" + doReset + " resetMask=" + resetMask + + "\n curLp=" + curLp + + "\n newLp=" + newLp); + } + + if (curLp.isIdenticalInterfaceName(newLp)) { + CompareAddressesResult car = curLp.compareAddresses(newLp); + if ((car.removed.size() != 0) || (car.added.size() != 0)) { + for (LinkAddress linkAddr : car.removed) { + if (linkAddr.getAddress() instanceof Inet4Address) { + resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES; + } + if (linkAddr.getAddress() instanceof Inet6Address) { + resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES; + } + } + if (DBG) { + log("handleConnectivityChange: addresses changed" + + " linkProperty[" + netType + "]:" + " resetMask=" + resetMask + + "\n car=" + car); + } + } else { + if (DBG) { + log("handleConnectivityChange: address are the same reset per doReset" + + " linkProperty[" + netType + "]:" + + " resetMask=" + resetMask); + } + } + } else { + resetMask = NetworkUtils.RESET_ALL_ADDRESSES; + log("handleConnectivityChange: interface not not equivalent reset both" + + " linkProperty[" + netType + "]:" + + " resetMask=" + resetMask); + } if (mNetConfigs[netType].isDefault()) { handleApplyDefaultProxy(netType); addDefaultRoute(mNetTrackers[netType]); @@ -1430,15 +1481,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - if (doReset) { + if (doReset || resetMask != 0) { LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties(); if (linkProperties != null) { String iface = linkProperties.getInterfaceName(); if (TextUtils.isEmpty(iface) == false) { - if (DBG) { - log("resetConnections(" + iface + ", NetworkUtils.RESET_ALL_ADDRESSES)"); - } - NetworkUtils.resetConnections(iface, NetworkUtils.RESET_ALL_ADDRESSES); + if (DBG) log("resetConnections(" + iface + ", " + resetMask + ")"); + NetworkUtils.resetConnections(iface, resetMask); } } }