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"); }