Stop using shared DUN APN when tethering stops.

Backported from master, including a bug fix and a cdma enhancement.

Even if other people are sharing the connection (ie, carrier wants
default and tethered traffic on the same APN) stop using a carrier-
described APN when the tethering stops.

bug:5972599
Change-Id: I25e4831855e6b62c0c3ab3a6f4d4846aaee6ac50
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 97fb0b0..b7dc4a2 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1031,9 +1031,14 @@
                 if ((ni.isConnectedOrConnecting() == true) &&
                         !network.isTeardownRequested()) {
                     if (ni.isConnected() == true) {
-                        // add the pid-specific dns
-                        handleDnsConfigurationChange(usedNetworkType);
-                        if (VDBG) log("special network already active");
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            // add the pid-specific dns
+                            handleDnsConfigurationChange(usedNetworkType);
+                            if (VDBG) log("special network already active");
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
                         return Phone.APN_ALREADY_ACTIVE;
                     }
                     if (VDBG) log("special network already connecting");
@@ -1221,6 +1226,7 @@
         }
 
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+            if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
             return false;
         }
         NetworkStateTracker tracker = mNetTrackers[networkType];
@@ -1233,11 +1239,16 @@
             }
             return false;
         }
+        final long token = Binder.clearCallingIdentity();
         try {
             InetAddress addr = InetAddress.getByAddress(hostAddress);
             LinkProperties lp = tracker.getLinkProperties();
             return addRouteToAddress(lp, addr);
-        } catch (UnknownHostException e) {}
+        } catch (UnknownHostException e) {
+            if (DBG) log("requestRouteToHostAddress got " + e.toString());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
         return false;
     }
 
@@ -1277,7 +1288,10 @@
 
     private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
             boolean doAdd, boolean toDefaultTable) {
-        if ((ifaceName == null) || (lp == null) || (r == null)) return false;
+        if ((ifaceName == null) || (lp == null) || (r == null)) {
+            if (DBG) log("modifyRoute got unexpected null: " + ifaceName + ", " + lp + ", " + r);
+            return false;
+        }
 
         if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
             loge("Error modifying route - too much recursion");
@@ -1309,7 +1323,7 @@
                 }
             } catch (Exception e) {
                 // never crash - catch them all
-                if (VDBG) loge("Exception trying to add a route: " + e);
+                if (DBG) loge("Exception trying to add a route: " + e);
                 return false;
             }
         } else {
@@ -1323,7 +1337,7 @@
                         mNetd.removeRoute(ifaceName, r);
                     } catch (Exception e) {
                         // never crash - catch them all
-                        if (VDBG) loge("Exception trying to remove a route: " + e);
+                        if (DBG) loge("Exception trying to remove a route: " + e);
                         return false;
                     }
                 } else {
@@ -1335,7 +1349,7 @@
                     mNetd.removeSecondaryRoute(ifaceName, r);
                 } catch (Exception e) {
                     // never crash - catch them all
-                    if (VDBG) loge("Exception trying to remove a route: " + e);
+                    if (DBG) loge("Exception trying to remove a route: " + e);
                     return false;
                 }
             }
@@ -2004,7 +2018,7 @@
                         mNetd.removeRoute(ifaceName, r);
                     } catch (Exception e) {
                         // never crash - catch them all
-                        if (VDBG) loge("Exception trying to remove a route: " + e);
+                        if (DBG) loge("Exception trying to remove a route: " + e);
                     }
                 }
             }
@@ -2218,7 +2232,7 @@
                 mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses));
                 mNetd.setDefaultInterfaceForDns(iface);
             } catch (Exception e) {
-                if (VDBG) loge("exception setting default dns interface: " + e);
+                if (DBG) loge("exception setting default dns interface: " + e);
             }
         }
         if (!domains.equals(SystemProperties.get("net.dns.search"))) {
@@ -2248,7 +2262,7 @@
                     mNetd.setDnsServersForInterface(p.getInterfaceName(),
                             NetworkUtils.makeStrings(dnses));
                 } catch (Exception e) {
-                    if (VDBG) loge("exception setting dns servers: " + e);
+                    if (DBG) loge("exception setting dns servers: " + e);
                 }
                 // set per-pid dns for attached secondary nets
                 List pids = mNetRequestersPids[netType];
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index ffe848d..d0e304f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -68,6 +68,8 @@
     private List<ApnContext> mApnList = null;
     PendingIntent mReconnectIntent = null;
 
+    private DataConnectionTracker mDataConnectionTracker = null;
+
     /**
      * Used internally for saving connecting parameters.
      */
@@ -202,6 +204,7 @@
     protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
     protected static final int EVENT_DISCONNECT = BASE + 4;
     protected static final int EVENT_RIL_CONNECTED = BASE + 5;
+    protected static final int EVENT_DISCONNECT_ALL = BASE + 6;
 
     //***** Tag IDs for EventLog
     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
@@ -234,10 +237,12 @@
 
 
    //***** Constructor
-    protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm) {
+    protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
+            DataConnectionTracker dct) {
         super(name);
         if (DBG) log("DataConnection constructor E");
         this.phone = phone;
+        this.mDataConnectionTracker = dct;
         mId = id;
         mRetryMgr = rm;
         this.cid = -1;
@@ -316,11 +321,19 @@
      *
      * @param dp is the DisconnectParams.
      */
-    private void notifyDisconnectCompleted(DisconnectParams dp) {
+    private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
         if (VDBG) log("NotifyDisconnectCompleted");
 
+        ApnContext alreadySent = null;
+        String reason = null;
+
         if (dp.onCompletedMsg != null) {
+            // Get ApnContext, but only valid on GSM devices this is a string on CDMA devices.
             Message msg = dp.onCompletedMsg;
+            if (msg.obj instanceof ApnContext) {
+                alreadySent = (ApnContext)msg.obj;
+            }
+            reason = dp.reason;
             if (VDBG) {
                 log(String.format("msg=%s msg.obj=%s", msg.toString(),
                     ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
@@ -328,6 +341,17 @@
             AsyncResult.forMessage(msg);
             msg.sendToTarget();
         }
+        if (sendAll) {
+            for (ApnContext a : mApnList) {
+                if (a == alreadySent) continue;
+                if (reason != null) a.setReason(reason);
+                Message msg = mDataConnectionTracker.obtainMessage(
+                        DataConnectionTracker.EVENT_DISCONNECT_DONE, a);
+                AsyncResult.forMessage(msg);
+                msg.sendToTarget();
+            }
+        }
+
         if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
     }
 
@@ -706,6 +730,13 @@
                     deferMessage(msg);
                     break;
 
+                case EVENT_DISCONNECT_ALL:
+                    if (DBG) {
+                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL" + mRefCount);
+                    }
+                    deferMessage(msg);
+                    break;
+
                 case EVENT_RIL_CONNECTED:
                     ar = (AsyncResult)msg.obj;
                     if (ar.exception == null) {
@@ -771,7 +802,7 @@
             }
             if (mDisconnectParams != null) {
                 if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
-                notifyDisconnectCompleted(mDisconnectParams);
+                notifyDisconnectCompleted(mDisconnectParams, true);
             }
             clearSettings();
         }
@@ -812,7 +843,13 @@
 
                 case EVENT_DISCONNECT:
                     if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
-                    notifyDisconnectCompleted((DisconnectParams)msg.obj);
+                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
+                    retVal = HANDLED;
+                    break;
+
+                case EVENT_DISCONNECT_ALL:
+                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
+                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
                     retVal = HANDLED;
                     break;
 
@@ -989,12 +1026,24 @@
                         transitionTo(mDisconnectingState);
                     } else {
                         if (msg.obj != null) {
-                            notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                            notifyDisconnectCompleted((DisconnectParams) msg.obj, false);
                         }
                     }
                     retVal = HANDLED;
                     break;
 
+                case EVENT_DISCONNECT_ALL:
+                    if (DBG) {
+                        log("DcActiveState msg.what=EVENT_DISCONNECT_ALL RefCount=" + mRefCount);
+                    }
+                    mRefCount = 0;
+                    DisconnectParams dp = (DisconnectParams) msg.obj;
+                    dp.tag = mTag;
+                    tearDownData(dp);
+                    transitionTo(mDisconnectingState);
+                    retVal = HANDLED;
+                    break;
+
                 default:
                     if (VDBG) {
                         log("DcActiveState not handled msg.what=0x" +
@@ -1124,4 +1173,16 @@
     public void tearDown(String reason, Message onCompletedMsg) {
         sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
     }
+
+    /**
+     * Tear down the connection through the apn on the network.  Ignores refcount and
+     * and always tears down.
+     *
+     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
+     *        With AsyncResult.userObj set to the original msg.obj.
+     */
+    public void tearDownAll(String reason, Message onCompletedMsg) {
+        sendMessage(obtainMessage(EVENT_DISCONNECT_ALL,
+                new DisconnectParams(reason, onCompletedMsg)));
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 04ba42d..863235b 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -1030,7 +1030,8 @@
                 }
             }
             if (didDisable) {
-                if (enabledCount == 0) {
+                if ((enabledCount == 0) || (apnId == APN_DUN_ID)) {
+                    mRequestedApnType = Phone.APN_TYPE_DEFAULT;
                     onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED);
                 }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index d55f346..a93d94f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
@@ -32,8 +33,9 @@
     private static final String LOG_TAG = "CDMA";
 
     // ***** Constructor
-    private CdmaDataConnection(CDMAPhone phone, String name, int id, RetryManager rm) {
-        super(phone, name, id, rm);
+    private CdmaDataConnection(CDMAPhone phone, String name, int id, RetryManager rm,
+            DataConnectionTracker dct) {
+        super(phone, name, id, rm, dct);
     }
 
     /**
@@ -44,12 +46,13 @@
      * @param rm the RetryManager
      * @return CdmaDataConnection that was created.
      */
-    static CdmaDataConnection makeDataConnection(CDMAPhone phone, int id, RetryManager rm) {
+    static CdmaDataConnection makeDataConnection(CDMAPhone phone, int id, RetryManager rm,
+            DataConnectionTracker dct) {
         synchronized (mCountLock) {
             mCount += 1;
         }
         CdmaDataConnection cdmaDc = new CdmaDataConnection(phone, "CdmaDC-" + mCount,
-                id, rm);
+                id, rm, dct);
         cdmaDc.start();
         if (DBG) cdmaDc.log("Made " + cdmaDc.getName());
         return cdmaDc;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index e2a4a7a..72ca3b5 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -122,7 +122,7 @@
 
     @Override
     public void dispose() {
-        cleanUpConnection(false, null);
+        cleanUpConnection(false, null, false);
 
         super.dispose();
 
@@ -277,7 +277,7 @@
      * @param tearDown true if the underlying DataConnection should be disconnected.
      * @param reason for the clean up.
      */
-    private void cleanUpConnection(boolean tearDown, String reason) {
+    private void cleanUpConnection(boolean tearDown, String reason, boolean doAll) {
         if (DBG) log("cleanUpConnection: reason: " + reason);
 
         // Clear the reconnect alarm, if set.
@@ -297,9 +297,15 @@
                 DataConnectionAc dcac =
                     mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
                 if (tearDown) {
-                    if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                    conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE,
-                            conn.getDataConnectionId(), 0, reason));
+                    if (doAll) {
+                        if (DBG) log("cleanUpConnection: teardown, conn.tearDownAll");
+                        conn.tearDownAll(reason, obtainMessage(EVENT_DISCONNECT_DONE,
+                                conn.getDataConnectionId(), 0, reason));
+                    } else {
+                        if (DBG) log("cleanUpConnection: teardown, conn.tearDown");
+                        conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE,
+                                conn.getDataConnectionId(), 0, reason));
+                    }
                     notificationDeferred = true;
                 } else {
                     if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
@@ -582,7 +588,7 @@
     @Override
     protected void onEnableNewApn() {
         // No mRequestedApnType check; only one connection is supported
-        cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
+        cleanUpConnection(true, Phone.REASON_APN_SWITCHED, false);
     }
 
     /**
@@ -764,13 +770,13 @@
     @Override
     protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
         // No apnId check; only one connection is supported
-        cleanUpConnection(tearDown, reason);
+        cleanUpConnection(tearDown, reason, (apnId == APN_DUN_ID));
     }
 
     @Override
     protected void onCleanUpAllConnections(String cause) {
         // Only one CDMA connection is supported
-        cleanUpConnection(true, cause);
+        cleanUpConnection(true, cause, false);
     }
 
     private void createAllDataConnectionList() {
@@ -789,7 +795,7 @@
             }
 
             int id = mUniqueIdGenerator.getAndIncrement();
-            dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm);
+            dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm, this);
             mDataConnections.put(id, dataConn);
             DataConnectionAc dcac = new DataConnectionAc(dataConn, LOG_TAG);
             int status = dcac.fullyConnectSync(mPhone.getContext(), this, dataConn.getHandler());
@@ -816,7 +822,7 @@
             notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED);
         } else {
             if (mState == State.FAILED) {
-                cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED);
+                cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED, false);
                 mDataConnections.get(0).resetRetryCount();
 
                 CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
@@ -895,7 +901,7 @@
                 log("onDataStateChanged: No active connection"
                         + "state is CONNECTED, disconnecting/cleanup");
                 writeEventLogCdmaDataDrop();
-                cleanUpConnection(true, null);
+                cleanUpConnection(true, null, false);
                 return;
             }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 1f24b58..1f5b7ab 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -22,6 +22,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.RILConstants;
@@ -38,8 +39,9 @@
     protected int mProfileId = RILConstants.DATA_PROFILE_DEFAULT;
     protected String mActiveApnType = Phone.APN_TYPE_DEFAULT;
     //***** Constructor
-    private GsmDataConnection(PhoneBase phone, String name, int id, RetryManager rm) {
-        super(phone, name, id, rm);
+    private GsmDataConnection(PhoneBase phone, String name, int id, RetryManager rm,
+            DataConnectionTracker dct) {
+        super(phone, name, id, rm, dct);
     }
 
     /**
@@ -50,11 +52,12 @@
      * @param rm the RetryManager
      * @return GsmDataConnection that was created.
      */
-    static GsmDataConnection makeDataConnection(PhoneBase phone, int id, RetryManager rm) {
+    static GsmDataConnection makeDataConnection(PhoneBase phone, int id, RetryManager rm,
+            DataConnectionTracker dct) {
         synchronized (mCountLock) {
             mCount += 1;
         }
-        GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDC-" + mCount, id, rm);
+        GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDC-" + mCount, id, rm, dct);
         gsmDc.start();
         if (DBG) gsmDc.log("Made " + gsmDc.getName());
         return gsmDc;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 9eaf7a0..95ea107 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -850,9 +850,28 @@
                 // Connection is still there. Try to clean up.
                 if (dcac != null) {
                     if (apnContext.getState() != State.DISCONNECTING) {
-                        if (DBG) log("cleanUpConnection: tearing down");
+                        boolean disconnectAll = false;
+                        if (Phone.APN_TYPE_DUN.equals(apnContext.getApnType())) {
+                            ApnSetting dunSetting = fetchDunApn();
+                            if (dunSetting != null &&
+                                    dunSetting.equals(apnContext.getApnSetting())) {
+                                if (DBG) log("tearing down dedicated DUN connection");
+                                // we need to tear it down - we brought it up just for dun and
+                                // other people are camped on it and now dun is done.  We need
+                                // to stop using it and let the normal apn list get used to find
+                                // connections for the remaining desired connections
+                                disconnectAll = true;
+                            }
+                        }
+                        if (DBG) {
+                            log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :""));
+                        }
                         Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
-                        apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
+                        if (disconnectAll) {
+                            apnContext.getDataConnection().tearDownAll(apnContext.getReason(), msg);
+                        } else {
+                            apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
+                        }
                         apnContext.setState(State.DISCONNECTING);
                     }
                 } else {
@@ -2213,7 +2232,7 @@
 
         RetryManager rm = new RetryManager();
         int id = mUniqueIdGenerator.getAndIncrement();
-        GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
+        GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm, this);
         mDataConnections.put(id, conn);
         DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG);
         int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());