am 3810361e: am 6d1d16c3: Merge "Remove STOPSHIP but allow seamless Handoff when possible." into honeycomb-LTE

* commit '3810361e4a3e93d3c8a10aaeec54f65dfcc2d03d':
  Remove STOPSHIP but allow seamless Handoff when possible.
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 19894a0..f2f0e82 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -52,11 +52,26 @@
 public class LinkProperties implements Parcelable {
 
     String mIfaceName;
-    private Collection<LinkAddress> mLinkAddresses;
-    private Collection<InetAddress> mDnses;
-    private Collection<RouteInfo> mRoutes;
+    private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
+    private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
+    private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
     private ProxyProperties mHttpProxy;
 
+    public static class CompareAddressesResult {
+        public ArrayList<LinkAddress> removed = new ArrayList<LinkAddress>();
+        public ArrayList<LinkAddress> added = new ArrayList<LinkAddress>();
+
+        @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 void clear() {
         mIfaceName = null;
-        mLinkAddresses = new ArrayList<LinkAddress>();
-        mDnses = new ArrayList<InetAddress>();
-        mRoutes = new ArrayList<RouteInfo>();
+        mLinkAddresses.clear();
+        mDnses.clear();
+        mRoutes.clear();
         mHttpProxy = null;
     }
 
@@ -155,6 +170,63 @@
         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<InetAddress> targetAddresses = target.getAddresses();
+        Collection<InetAddress> 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<InetAddress> 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<RouteInfo> 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 @@
 
         if (!(obj instanceof LinkProperties)) return false;
 
-        boolean sameAddresses;
-        boolean sameDnses;
-        boolean sameRoutes;
-
         LinkProperties target = (LinkProperties) obj;
 
-        Collection<InetAddress> targetAddresses = target.getAddresses();
-        Collection<InetAddress> sourceAddresses = getAddresses();
-        sameAddresses = (sourceAddresses.size() == targetAddresses.size()) ?
-                sourceAddresses.containsAll(targetAddresses) : false;
+        return isIdenticalInterfaceName(target) &&
+                isIdenticalAddresses(target) &&
+                isIdenticalDnses(target) &&
+                isIdenticalRoutes(target) &&
+                isIdenticalHttpProxy(target);
+    }
 
-        Collection<InetAddress> targetDnses = target.getDnses();
-        sameDnses = (mDnses.size() == targetDnses.size()) ?
-                mDnses.containsAll(targetDnses) : false;
-
-        Collection<RouteInfo> 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<LinkAddress>(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 41450d2..68cdeee 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -34,6 +34,7 @@
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.LinkProperties;
+import android.net.LinkProperties.CompareAddressesResult;
 import android.net.MobileDataStateTracker;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
@@ -76,6 +77,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -92,6 +94,7 @@
 public class ConnectivityService extends IConnectivityManager.Stub {
 
     private static final boolean DBG = true;
+    private static final boolean VDBG = true;
     private static final String TAG = "ConnectivityService";
 
     private static final boolean LOGD_RULES = false;
@@ -126,6 +129,11 @@
     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
      */
@@ -332,6 +340,7 @@
 
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
+        mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1];
 
         mNetworkPreference = getPersistedNetworkPreference();
 
@@ -468,6 +477,7 @@
                         mNetConfigs[netType].radio);
                 continue;
             }
+            mCurrentLinkProperties[netType] = mNetTrackers[netType].getLinkProperties();
         }
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -1563,6 +1573,8 @@
      * 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.
@@ -1570,6 +1582,45 @@
         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]);
@@ -1597,15 +1648,13 @@
             }
         }
 
-        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 fba3184..a69ce8b 100644
--- a/telephony/java/com/android/internal/telephony/DataCallState.java
+++ b/telephony/java/com/android/internal/telephony/DataCallState.java
@@ -52,7 +52,7 @@
     /**
      * 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 5c84fdc..1bba8e3 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 @@
         } 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 @@
         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<LinkAddress> oLinks = mLinkProperties.getLinkAddresses();
-        Collection<LinkAddress> 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 @@
                 }
                 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 @@
                 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<ApnContext>(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 309dbed..9e185e5 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 @@
         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 @@
             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 df5898b..bf964b7 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.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.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 @@
 
     /**
      * @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<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) {
@@ -1091,32 +1095,30 @@
         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<DataCallState, DataConnectionAc> response;
-        response = new HashMap<DataCallState, DataConnectionAc>();
+        HashMap<DataCallState, DataConnectionAc> dataCallStateToDcac;
+        dataCallStateToDcac = new HashMap<DataCallState, DataConnectionAc>();
         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<ApnContext> apnsToClear = findApnContextToClean(response.values());
+        // A list of apns to cleanup, those that aren't in the list we know we have to cleanup
+        List<ApnContext> 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<ApnContext> apns = dcac.getApnListSync();
 
-            // filter out ApnContext with "Connected/Connecting" state.
+            // Find which ApnContexts of this DC are in the "Connected/Connecting" state.
             ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>();
             for (ApnContext apnContext : apns) {
                 if (apnContext.getState() == State.CONNECTED ||
@@ -1125,67 +1127,86 @@
                     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");
     }