From e822225f7a01ef088ff01f7613f953d5d89945ed 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 ++++++- .../internal/telephony/DataCallState.java | 2 +- .../internal/telephony/DataConnection.java | 82 +++++----- .../internal/telephony/DataConnectionAc.java | 14 +- .../gsm/GsmDataConnectionTracker.java | 145 ++++++++++-------- 6 files changed, 291 insertions(+), 148 deletions(-) diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 19894a039a156..f2f0e825c0074 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 42be7565e0175..812c1eb1d547e 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); } } } diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java index fba3184a2e025..a69ce8b7fe603 100644 --- a/telephony/java/com/android/internal/telephony/DataCallState.java +++ b/telephony/java/com/android/internal/telephony/DataCallState.java @@ -52,7 +52,7 @@ public class DataCallState { /** * Class returned by onSetupConnectionCompleted. */ - protected enum SetupResult { + public enum SetupResult { SUCCESS, ERR_BadCommand, ERR_UnacceptableParameter, diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 5c84fdc2b7360..1bba8e3a99c56 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -17,30 +17,25 @@ package com.android.internal.telephony; +import com.android.internal.telephony.DataCallState.SetupResult; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import android.app.PendingIntent; -import android.net.LinkAddress; import android.net.LinkCapabilities; import android.net.LinkProperties; -import android.net.NetworkUtils; +import android.net.LinkProperties.CompareAddressesResult; import android.net.ProxyProperties; import android.os.AsyncResult; -import android.os.Bundle; import android.os.Message; -import android.os.Parcel; -import android.os.Parcelable; import android.os.SystemProperties; import android.text.TextUtils; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; /** * {@hide} @@ -497,8 +492,7 @@ public abstract class DataConnection extends StateMachine { } else { if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response); cid = response.cid; - // set link properties based on data call response - result = setLinkProperties(response, mLinkProperties); + result = updateLinkProperty(response).setupResult; } return result; @@ -527,48 +521,41 @@ public abstract class DataConnection extends StateMachine { return response.setLinkProperties(lp, okToUseSystemPropertyDns); } - private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty( - DataCallState newState) { - DataConnectionAc.LinkPropertyChangeAction changed = - DataConnectionAc.LinkPropertyChangeAction.NONE; + public static class UpdateLinkPropertyResult { + public DataCallState.SetupResult setupResult = DataCallState.SetupResult.SUCCESS; + public LinkProperties oldLp; + public LinkProperties newLp; + public UpdateLinkPropertyResult(LinkProperties curLp) { + oldLp = curLp; + newLp = curLp; + } + } - if (newState == null) return changed; + private UpdateLinkPropertyResult updateLinkProperty(DataCallState newState) { + UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties); - DataCallState.SetupResult result; - LinkProperties newLp = new LinkProperties(); + if (newState == null) return result; + + DataCallState.SetupResult setupResult; + result.newLp = new LinkProperties(); // set link properties based on data call response - result = setLinkProperties(newState, newLp); - if (result != DataCallState.SetupResult.SUCCESS) { - if (DBG) log("UpdateLinkProperty failed : " + result); - return changed; + result.setupResult = setLinkProperties(newState, result.newLp); + if (result.setupResult != DataCallState.SetupResult.SUCCESS) { + if (DBG) log("updateLinkProperty failed : " + result.setupResult); + return result; } // copy HTTP proxy as it is not part DataCallState. - newLp.setHttpProxy(mLinkProperties.getHttpProxy()); + result.newLp.setHttpProxy(mLinkProperties.getHttpProxy()); - if (DBG) log("old LP=" + mLinkProperties); - if (DBG) log("new LP=" + newLp); - - // Check consistency of link address. Currently we expect - // only one "global" address is assigned per each IP type. - Collection oLinks = mLinkProperties.getLinkAddresses(); - Collection nLinks = newLp.getLinkAddresses(); - for (LinkAddress oldLink : oLinks) { - for (LinkAddress newLink : nLinks) { - if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(), - newLink.getAddress())) && - (oldLink.equals(newLink) == false)) { - return DataConnectionAc.LinkPropertyChangeAction.RESET; - } - } + if (DBG && (! result.oldLp.equals(result.newLp))) { + if (DBG) log("updateLinkProperty old != new"); + if (VDBG) log("updateLinkProperty old LP=" + result.oldLp); + if (VDBG) log("updateLinkProperty new LP=" + result.newLp); } + mLinkProperties = result.newLp; - if (mLinkProperties == null || !mLinkProperties.equals(newLp)) { - mLinkProperties = newLp; - changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED; - } - - return changed; + return result; } /** @@ -643,14 +630,15 @@ public abstract class DataConnection extends StateMachine { } case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: { DataCallState newState = (DataCallState) msg.obj; - DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState); + UpdateLinkPropertyResult result = + updateLinkProperty(newState); if (VDBG) { - log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action=" - + action + " newState=" + newState); + log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE result=" + + result + " newState=" + newState); } mAc.replyToMessage(msg, DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, - action.ordinal()); + result); break; } case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: { @@ -688,7 +676,7 @@ public abstract class DataConnection extends StateMachine { case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: { if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size()); mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST, - new ArrayList(mApnList)); + new ArrayList(mApnList)); break; } case DataConnectionAc.REQ_SET_RECONNECT_INTENT: { diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java index 309dbed669baf..9e185e50ec632 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java @@ -16,12 +16,14 @@ package com.android.internal.telephony; +import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import android.app.PendingIntent; import android.net.LinkCapabilities; import android.net.LinkProperties; +import android.net.LinkProperties.CompareAddressesResult; import android.net.ProxyProperties; import android.os.Message; @@ -310,18 +312,18 @@ public class DataConnectionAc extends AsyncChannel { if (DBG) log("reqUpdateLinkPropertiesDataCallState"); } - public LinkPropertyChangeAction rspUpdateLinkPropertiesDataCallState(Message response) { - LinkPropertyChangeAction retVal = LinkPropertyChangeAction.fromInt(response.arg1); - if (DBG) log("rspUpdateLinkPropertiesState=" + retVal); + public UpdateLinkPropertyResult rspUpdateLinkPropertiesDataCallState(Message response) { + UpdateLinkPropertyResult retVal = (UpdateLinkPropertyResult)response.obj; + if (DBG) log("rspUpdateLinkPropertiesState: retVal=" + retVal); return retVal; } /** * Update link properties in the data connection * - * @return true if link property has been updated. false otherwise. + * @return the removed and added addresses. */ - public LinkPropertyChangeAction updateLinkPropertiesDataCallStateSync(DataCallState newState) { + public UpdateLinkPropertyResult updateLinkPropertiesDataCallStateSync(DataCallState newState) { Message response = sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState); if ((response != null) && @@ -329,7 +331,7 @@ public class DataConnectionAc extends AsyncChannel { return rspUpdateLinkPropertiesDataCallState(response); } else { log("getLinkProperties error response=" + response); - return LinkPropertyChangeAction.NONE; + return new UpdateLinkPropertyResult(new LinkProperties()); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index df5898bc0cb72..bf964b7befe79 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -26,6 +26,9 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.ConnectivityManager; +import android.net.LinkAddress; +import android.net.LinkProperties.CompareAddressesResult; +import android.net.NetworkUtils; import android.net.ProxyProperties; import android.net.TrafficStats; import android.net.Uri; @@ -53,6 +56,7 @@ import com.android.internal.telephony.ApnContext; import com.android.internal.telephony.ApnSetting; import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult; import com.android.internal.telephony.DataConnectionAc; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.Phone; @@ -1037,7 +1041,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { /** * @param dcacs Collection of DataConnectionAc reported from RIL. - * @return List of ApnContext whihc is connected, but does not present in + * @return List of ApnContext which is connected, but is not present in * data connection list reported from RIL. */ private List findApnContextToClean(Collection dcacs) { @@ -1091,32 +1095,30 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size()); // Create a hash map to store the dataCallState of each DataConnectionAc - // TODO: Depends on how frequent the DATA_CALL_LIST got updated, - // may cache response to reduce comparison. - HashMap response; - response = new HashMap(); + HashMap dataCallStateToDcac; + dataCallStateToDcac = new HashMap(); for (DataCallState dataCallState : dataCallStates) { DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid); - if (dcac != null) response.put(dataCallState, dcac); + if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac); } - // step1: Find a list of "connected" APN which does not have reference to - // calls listed in the Data Call List. - List apnsToClear = findApnContextToClean(response.values()); + // A list of apns to cleanup, those that aren't in the list we know we have to cleanup + List apnsToCleanup = findApnContextToClean(dataCallStateToDcac.values()); - // step2: Check status of each calls in Data Call List. - // Collect list of ApnContext associated with the data call if the link - // has to be cleared. + // Find which connections have changed state and send a notification or cleanup for (DataCallState newState : dataCallStates) { - DataConnectionAc dcac = response.get(newState); + DataConnectionAc dcac = dataCallStateToDcac.get(newState); - // no associated DataConnection found. Ignore. - if (dcac == null) continue; + if (dcac == null) { + loge("onDataStateChanged(ar): No associated DataConnection ignore"); + continue; + } + // The list of apn's associated with this DataConnection Collection apns = dcac.getApnListSync(); - // filter out ApnContext with "Connected/Connecting" state. + // Find which ApnContexts of this DC are in the "Connected/Connecting" state. ArrayList connectedApns = new ArrayList(); for (ApnContext apnContext : apns) { if (apnContext.getState() == State.CONNECTED || @@ -1125,67 +1127,86 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { connectedApns.add(apnContext); } } - - // No "Connected" ApnContext associated with this CID. Ignore. - if (connectedApns.isEmpty()) { - continue; - } - - if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid - + " newState=" + newState.toString()); - if (newState.active != 0) { - boolean resetConnection; - switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) { - case NONE: - if (DBG) log("onDataStateChanged(ar): Found but no change, skip"); - resetConnection = false; - break; - case CHANGED: - for (ApnContext apnContext : connectedApns) { - if (DBG) log("onDataStateChanged(ar): Found and changed, notify (" + - apnContext.toString() + ")"); - mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED, - apnContext.getApnType()); + if (connectedApns.size() == 0) { + if (DBG) log("onDataStateChanged(ar): no connected apns"); + } else { + // Determine if the connection/apnContext should be cleaned up + // or just a notification should be sent out. + if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid + + " newState=" + newState.toString()); + if (newState.active == 0) { + if (DBG) { + log("onDataStateChanged(ar): inactive, cleanup apns=" + connectedApns); } - // Temporary hack, at this time a transition from CDMA -> Global - // fails so we'll hope for the best and not reset the connection. - // @see bug/4455071 - if (SystemProperties.getBoolean("telephony.ignore-state-changes", - true)) { - log("onDataStateChanged(ar): STOPSHIP don't reset, continue"); - resetConnection = false; + apnsToCleanup.addAll(connectedApns); + } else { + // Its active so update the DataConnections link properties + UpdateLinkPropertyResult result = + dcac.updateLinkPropertiesDataCallStateSync(newState); + if (result.oldLp.equals(result.newLp)) { + if (DBG) log("onDataStateChanged(ar): no change"); } else { - // Things changed so reset connection, when hack is removed - // this is the normal path. - log("onDataStateChanged(ar): changed so resetting connection"); - resetConnection = true; + if (result.oldLp.isIdenticalInterfaceName(result.newLp)) { + if (! result.oldLp.isIdenticalDnses(result.newLp) || + ! result.oldLp.isIdenticalRoutes(result.newLp) || + ! result.oldLp.isIdenticalHttpProxy(result.newLp) || + ! result.oldLp.isIdenticalAddresses(result.newLp)) { + // If the same address type was removed and added we need to cleanup + CompareAddressesResult car = + result.oldLp.compareAddresses(result.newLp); + boolean needToClean = false; + for (LinkAddress added : car.added) { + for (LinkAddress removed : car.removed) { + if (NetworkUtils.addressTypeMatches(removed.getAddress(), + added.getAddress())) { + needToClean = true; + break; + } + } + } + if (needToClean) { + if (DBG) { + log("onDataStateChanged(ar): addr change, cleanup apns=" + + connectedApns); + } + apnsToCleanup.addAll(connectedApns); + } else { + if (DBG) log("onDataStateChanged(ar): simple change"); + for (ApnContext apnContext : connectedApns) { + mPhone.notifyDataConnection( + Phone.REASON_LINK_PROPERTIES_CHANGED, + apnContext.getApnType()); + } + } + } else { + if (DBG) { + log("onDataStateChanged(ar): no changes"); + } + } + } else { + if (DBG) { + log("onDataStateChanged(ar): interface change, cleanup apns=" + + connectedApns); + } + apnsToCleanup.addAll(connectedApns); + } } - break; - case RESET: - default: - if (DBG) log("onDataStateChanged(ar): an error, reset connection"); - resetConnection = true; - break; } - if (resetConnection == false) continue; } - - if (DBG) log("onDataStateChanged(ar): reset connection."); - - apnsToClear.addAll(connectedApns); } - // step3: Clear apn connection if applicable. - if (!apnsToClear.isEmpty()) { + if (apnsToCleanup.size() != 0) { // Add an event log when the network drops PDP int cid = getCellLocationId(); EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid, TelephonyManager.getDefault().getNetworkType()); } - for (ApnContext apnContext : apnsToClear) { + // Cleanup those dropped connections + for (ApnContext apnContext : apnsToCleanup) { cleanUpConnection(true, apnContext); } + if (DBG) log("onDataStateChanged(ar): X"); }