Clean up the switch state machine.
Robert Greenwalt [Tue, 7 Apr 2015 20:47:39 +0000 (13:47 -0700)]
Handle inputs while in any state.  This is made much easier by removing
the synchronous calls - any time you find a synchronous call into a state
machine you're doing something wrong.

Also rework the timeout system - it was bad in that it injected the
new session id whenever you start an new session so old timers were
always acted upon.

Also should eliminate/reduce the duplicate ALLOW_DATA RIL calls.

bug:20076867
Change-Id: Ibabd76fad0759dd9c403ac8ee017958e38df56a5

src/java/com/android/internal/telephony/ProxyController.java
src/java/com/android/internal/telephony/RIL.java
src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
src/java/com/android/internal/telephony/dataconnection/DctController.java

index a4887cd..da85598 100644 (file)
@@ -52,6 +52,7 @@ public class ProxyController {
     private static final int EVENT_START_RC_RESPONSE        = 2;
     private static final int EVENT_APPLY_RC_RESPONSE        = 3;
     private static final int EVENT_FINISH_RC_RESPONSE       = 4;
+    private static final int EVENT_TIMEOUT                  = 5;
 
     private static final int SET_RC_STATUS_IDLE             = 0;
     private static final int SET_RC_STATUS_STARTING         = 1;
@@ -109,8 +110,6 @@ public class ProxyController {
     private int[] mNewRadioAccessFamily;
     private int[] mOldRadioAccessFamily;
 
-    // runnable for radio capability request timeout handling
-    RadioCapabilityRunnable mSetRadioCapabilityRunnable;
 
     //***** Class Methods
     public static ProxyController getInstance(Context context, PhoneProxy[] phoneProxy,
@@ -144,8 +143,6 @@ public class ProxyController {
         mCurrentLogicalModemIds = new String[mProxyPhones.length];
         mNewLogicalModemIds = new String[mProxyPhones.length];
 
-        mSetRadioCapabilityRunnable = new RadioCapabilityRunnable();
-
         // wake lock for set radio capability
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
@@ -257,8 +254,8 @@ public class ProxyController {
 
         // Start timer to make sure all phones respond within a specific time interval.
         // Will send FINISH if a timeout occurs.
-        mSetRadioCapabilityRunnable.setTimeoutState(mRadioCapabilitySessionId);
-        mHandler.postDelayed(mSetRadioCapabilityRunnable, SET_RC_TIMEOUT_WAITING_MSEC);
+        Message msg = mHandler.obtainMessage(EVENT_TIMEOUT, mRadioCapabilitySessionId, 0);
+        mHandler.sendMessageDelayed(msg, SET_RC_TIMEOUT_WAITING_MSEC);
 
         synchronized (mSetRadioAccessFamilyStatus) {
             logd("setRadioCapability: new request session id=" + mRadioCapabilitySessionId);
@@ -317,6 +314,10 @@ public class ProxyController {
                     onFinishRadioCapabilityResponse(msg);
                     break;
 
+                case EVENT_TIMEOUT:
+                    onTimeoutRadioCapability(msg);
+                    break;
+
                 default:
                     break;
             }
@@ -435,13 +436,13 @@ public class ProxyController {
             } else {
                 logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=SUCCESS");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_SUCCESS;
+                // The modems may have been restarted and forgotten this
+                mDctController.retryAttach(id);
                 mProxyPhones[id].radioCapabilityUpdated(rc);
             }
 
             mRadioAccessFamilyStatusCounter--;
             if (mRadioAccessFamilyStatusCounter == 0) {
-                logd("onNotificationRadioCapabilityChanged: removing callback from handler");
-                mHandler.removeCallbacks(mSetRadioCapabilityRunnable);
                 logd("onNotificationRadioCapabilityChanged: APPLY URC success=" +
                         mTransactionFailed);
                 issueFinish(mRadioCapabilitySessionId);
@@ -470,6 +471,29 @@ public class ProxyController {
         }
     }
 
+    private void onTimeoutRadioCapability(Message msg) {
+        if (msg.arg1 != mRadioCapabilitySessionId) {
+           logd("RadioCapability timeout: Ignore msg.arg1=" + msg.arg1 +
+                   "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId);
+            return;
+        }
+
+        synchronized(mSetRadioAccessFamilyStatus) {
+            // timed-out.  Clean up as best we can
+            for (int i = 0; i < mProxyPhones.length; i++) {
+                logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" +
+                        mSetRadioAccessFamilyStatus[i]);
+            }
+
+            // Increment the sessionId as we are completing the transaction below
+            // so we don't want it completed when the FINISH phase is done.
+            int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
+            // send FINISH request with fail status and then uniqueDifferentId
+            mTransactionFailed = true;
+            issueFinish(uniqueDifferentId);
+        }
+    }
+
     private void issueFinish(int sessionId) {
         // Issue FINISH
         synchronized(mSetRadioAccessFamilyStatus) {
@@ -511,6 +535,9 @@ public class ProxyController {
             intent.putParcelableArrayListExtra(TelephonyIntents.EXTRA_RADIO_ACCESS_FAMILY,
                     phoneRAFList);
 
+            // make messages about the old transaction obsolete (specifically the timeout)
+            mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+
             // Reinitialize
             clearTransaction();
         } else {
@@ -559,50 +586,6 @@ public class ProxyController {
                 requestRC, mHandler.obtainMessage(eventId));
     }
 
-    /**
-     * RadioCapabilityRunnable is used to check
-     * if radio capability request's response is out of date.
-     * <p>
-     * Note that the setRadioCapability will be stopped directly and send FINISH
-     * with fail status to all logical modems. and send out fail intent
-     *
-     */
-    private class RadioCapabilityRunnable implements Runnable {
-        private int mSessionId;
-        public  RadioCapabilityRunnable() {
-        }
-
-        public void setTimeoutState(int sessionId) {
-            mSessionId = sessionId;
-        }
-
-        @Override
-        public void run() {
-            if (mSessionId != mRadioCapabilitySessionId) {
-                logd("RadioCapability timeout: Ignore mSessionId=" + mSessionId
-                       + "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId);
-                return;
-            }
-
-            synchronized(mSetRadioAccessFamilyStatus) {
-                // timed-out.  Clean up as best we can
-                for (int i = 0; i < mProxyPhones.length; i++) {
-                    logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" +
-                            mSetRadioAccessFamilyStatus[i]);
-                }
-
-                // Increment the sessionId as we are completing the transaction below
-                // so we don't want it completed when the FINISH phase is done.
-                int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
-
-                // send FINISH request with fail status and then uniqueDifferentId
-                mTransactionFailed = true;
-                issueFinish(uniqueDifferentId);
-                completeRadioCapabilityTransaction();
-            }
-        }
-    }
-
     // This method will return max number of raf bits supported from the raf
     // values currently stored in all phone objects
     public int getMaxRafSupported() {
index 163ad39..ccd24cb 100644 (file)
@@ -712,7 +712,10 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     // FIXME This API should take an AID and slot ID
     public void setDataAllowed(boolean allowed, Message result) {
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_ALLOW_DATA, result);
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) +
+                    " allowed: " + allowed);
+        }
 
         rr.mParcel.writeInt(1);
         rr.mParcel.writeInt(allowed ? 1 : 0);
index 95e3ab7..57f0597 100644 (file)
@@ -34,28 +34,22 @@ public class DcSwitchAsyncChannel extends AsyncChannel {
 
     // ***** Event codes for driving the state machine
     private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00002000;
-    static final int REQ_CONNECT = BASE + 0;
-    static final int RSP_CONNECT = BASE + 1;
-    static final int REQ_DISCONNECT = BASE + 2;
-    static final int RSP_DISCONNECT = BASE + 3;
-    static final int REQ_DISCONNECT_ALL = BASE + 4;
-    static final int RSP_DISCONNECT_ALL = BASE + 5;
-    static final int REQ_IS_IDLE_STATE = BASE + 6;
-    static final int RSP_IS_IDLE_STATE = BASE + 7;
-    static final int REQ_IS_IDLE_OR_DETACHING_STATE = BASE + 8;
-    static final int RSP_IS_IDLE_OR_DETACHING_STATE = BASE + 9;
-    static final int EVENT_DATA_ATTACHED = BASE + 10;
-    static final int EVENT_DATA_DETACHED = BASE + 11;
+    static final int REQ_CONNECT =                    BASE + 0;
+    static final int REQ_RETRY_CONNECT  =             BASE + 1;
+    static final int REQ_DISCONNECT_ALL =             BASE + 2;
+    static final int REQ_IS_IDLE_STATE =              BASE + 3;
+    static final int RSP_IS_IDLE_STATE =              BASE + 4;
+    static final int REQ_IS_IDLE_OR_DETACHING_STATE = BASE + 5;
+    static final int RSP_IS_IDLE_OR_DETACHING_STATE = BASE + 6;
+    static final int EVENT_DATA_ATTACHED =            BASE + 7;
+    static final int EVENT_DATA_DETACHED =            BASE + 8;
 
     private static final int CMD_TO_STRING_COUNT = EVENT_DATA_DETACHED - BASE + 1;
     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
     static {
         sCmdToString[REQ_CONNECT - BASE] = "REQ_CONNECT";
-        sCmdToString[RSP_CONNECT - BASE] = "RSP_CONNECT";
-        sCmdToString[REQ_DISCONNECT - BASE] = "REQ_DISCONNECT";
-        sCmdToString[RSP_DISCONNECT - BASE] = "RSP_DISCONNECT";
+        sCmdToString[REQ_RETRY_CONNECT - BASE] = "REQ_RETRY_CONNECT";
         sCmdToString[REQ_DISCONNECT_ALL - BASE] = "REQ_DISCONNECT_ALL";
-        sCmdToString[RSP_DISCONNECT_ALL - BASE] = "RSP_DISCONNECT_ALL";
         sCmdToString[REQ_IS_IDLE_STATE - BASE] = "REQ_IS_IDLE_STATE";
         sCmdToString[RSP_IS_IDLE_STATE - BASE] = "RSP_IS_IDLE_STATE";
         sCmdToString[REQ_IS_IDLE_OR_DETACHING_STATE - BASE] = "REQ_IS_IDLE_OR_DETACHING_STATE";
@@ -95,52 +89,18 @@ public class DcSwitchAsyncChannel extends AsyncChannel {
         tagId = id;
     }
 
-    private int rspConnect(Message response) {
-        int retVal = response.arg1;
-        if (DBG) log("rspConnect=" + retVal);
-        return retVal;
-    }
-
-    public int connectSync(RequestInfo apnRequest) {
-        Message response = sendMessageSynchronously(REQ_CONNECT, apnRequest);
-        if ((response != null) && (response.what == RSP_CONNECT)) {
-            return rspConnect(response);
-        } else {
-            if (DBG) log("rspConnect error response=" + response);
-            return PhoneConstants.APN_REQUEST_FAILED;
-        }
+    public int connect(RequestInfo apnRequest) {
+        sendMessage(REQ_CONNECT, apnRequest);
+        return PhoneConstants.APN_REQUEST_STARTED;
     }
 
-    private int rspDisconnect(Message response) {
-        int retVal = response.arg1;
-        if (DBG) log("rspDisconnect=" + retVal);
-        return retVal;
+    public void retryConnect() {
+        sendMessage(REQ_RETRY_CONNECT);
     }
 
-    public int disconnectSync(RequestInfo apnRequest) {
-        Message response = sendMessageSynchronously(REQ_DISCONNECT, apnRequest);
-        if ((response != null) && (response.what == RSP_DISCONNECT)) {
-            return rspDisconnect(response);
-        } else {
-            if (DBG) log("rspDisconnect error response=" + response);
-            return PhoneConstants.APN_REQUEST_FAILED;
-        }
-    }
-
-    private int rspDisconnectAll(Message response) {
-        int retVal = response.arg1;
-        if (DBG) log("rspDisconnectAll=" + retVal);
-        return retVal;
-    }
-
-    public int disconnectAllSync() {
-        Message response = sendMessageSynchronously(REQ_DISCONNECT_ALL);
-        if ((response != null) && (response.what == RSP_DISCONNECT_ALL)) {
-            return rspDisconnectAll(response);
-        } else {
-            if (DBG) log("rspDisconnectAll error response=" + response);
-            return PhoneConstants.APN_REQUEST_FAILED;
-        }
+    public int disconnectAll() {
+        sendMessage(REQ_DISCONNECT_ALL);
+        return PhoneConstants.APN_REQUEST_STARTED;
     }
 
     public void notifyDataAttached() {
index ed87a99..5166b68 100644 (file)
@@ -26,6 +26,7 @@ import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
 
+import android.os.AsyncResult;
 import android.os.Message;
 import android.telephony.Rlog;
 
@@ -36,7 +37,9 @@ public class DcSwitchStateMachine extends StateMachine {
 
     // ***** Event codes for driving the state machine
     private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00001000;
-    private static final int EVENT_CONNECTED = BASE + 0;
+    private static final int EVENT_CONNECTED    = BASE + 0;
+    private static final int EVENT_DATA_ALLOWED = BASE + 1;
+    private static final int CMD_RETRY_ATTACH   = BASE + 2;
 
     private int mId;
     private Phone mPhone;
@@ -89,21 +92,24 @@ public class DcSwitchStateMachine extends StateMachine {
 
             switch (msg.what) {
                 case DcSwitchAsyncChannel.REQ_CONNECT: {
-                    if (DBG) {
-                        log("IdleState: REQ_CONNECT");
-                    }
+                    RequestInfo apnRequest = (RequestInfo)msg.obj;
+                    if (DBG) log("IdleState: REQ_CONNECT, apnRequest=" + apnRequest);
+                    transitionTo(mAttachingState);
+                    retVal = HANDLED;
+                    break;
+                }
 
-                    PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
-                    pb.mCi.setDataAllowed(true, null);
+                case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
+                    if (DBG) log("AttachingState: REQ_DISCONNECT_ALL" );
 
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
+                    // Shouldn't have any requests, but why not try..
+                    DctController.getInstance().releaseAllRequests(mId);
 
-                    transitionTo(mAttachingState);
                     retVal = HANDLED;
                     break;
                 }
 
+
                 case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
                     if (DBG) {
                         log("AttachingState: EVENT_DATA_ATTACHED");
@@ -132,9 +138,17 @@ public class DcSwitchStateMachine extends StateMachine {
     }
 
     private class AttachingState extends State {
+        private int mCurrentAllowedSequence = 0;
         @Override
         public void enter() {
             log("AttachingState: enter");
+            doEnter();
+        }
+
+        private void doEnter() {
+            final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+            pb.mCi.setDataAllowed(true, obtainMessage(EVENT_DATA_ALLOWED,
+                    ++mCurrentAllowedSequence, 0));
         }
 
         @Override
@@ -143,36 +157,51 @@ public class DcSwitchStateMachine extends StateMachine {
 
             switch (msg.what) {
                 case DcSwitchAsyncChannel.REQ_CONNECT: {
-                    if (DBG) {
-                        log("AttachingState: REQ_CONNECT");
-                    }
+                    RequestInfo apnRequest = (RequestInfo)msg.obj;
+                    if (DBG) log("AttachingState: REQ_CONNECT, apnRequest=" + apnRequest);
 
-                    PhoneBase pb = (PhoneBase) ((PhoneProxy) mPhone).getActivePhone();
-                    pb.mCi.setDataAllowed(true, null);
+                    // do nothing - wait til we attach and then we'll execute all requests
+                    retVal = HANDLED;
+                    break;
+                }
 
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
+                case EVENT_DATA_ALLOWED: {
+                    AsyncResult ar = (AsyncResult)msg.obj;
+                    if (mCurrentAllowedSequence != msg.arg1) {
+                        loge("EVENT_DATA_ATTACHED ignored arg1=" + msg.arg1 + ", seq=" +
+                                mCurrentAllowedSequence);
+                    } else if (ar.exception != null) {
+                        loge("EVENT_DATA_ATTACHED failed, " + ar.exception);
+                        transitionTo(mIdleState);
+                    }
                     retVal = HANDLED;
                     break;
                 }
 
-                case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
+                case DcSwitchAsyncChannel.REQ_RETRY_CONNECT: {
+                    if (DBG) log("AttachingState going to retry");
+                    doEnter();
+                    retVal = HANDLED;
+                    break;
+                }
+
+                case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED: {
                     if (DBG) {
                         log("AttachingState: EVENT_DATA_ATTACHED");
                     }
                     transitionTo(mAttachedState);
                     retVal = HANDLED;
                     break;
+                }
 
                 case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
                     if (DBG) {
                         log("AttachingState: REQ_DISCONNECT_ALL" );
                     }
-                    DctController.getInstance().releaseAllRequests(mId);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
-                            PhoneConstants.APN_REQUEST_STARTED);
 
-                    transitionTo(mDetachingState);
+                    // modem gets unhappy if we try to detach while attaching
+                    // wait til attach finishes.
+                    deferMessage(msg);
                     retVal = HANDLED;
                     break;
                 }
@@ -204,27 +233,9 @@ public class DcSwitchStateMachine extends StateMachine {
             switch (msg.what) {
                 case DcSwitchAsyncChannel.REQ_CONNECT: {
                     RequestInfo apnRequest = (RequestInfo)msg.obj;
-                    if (DBG) {
-                        log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
-                    }
+                    if (DBG) log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
 
                     DctController.getInstance().executeRequest(apnRequest);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
-                    retVal = HANDLED;
-                    break;
-                }
-                case DcSwitchAsyncChannel.REQ_DISCONNECT: {
-                    RequestInfo apnRequest = (RequestInfo)msg.obj;
-                    if (DBG) {
-                        log("AttachedState: REQ_DISCONNECT apnRequest=" + apnRequest);
-                    }
-
-                    DctController.getInstance().releaseRequest(apnRequest);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
                     retVal = HANDLED;
                     break;
                 }
@@ -234,9 +245,6 @@ public class DcSwitchStateMachine extends StateMachine {
                         log("AttachedState: REQ_DISCONNECT_ALL" );
                     }
                     DctController.getInstance().releaseAllRequests(mId);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
                     transitionTo(mDetachingState);
                     retVal = HANDLED;
                     break;
@@ -277,6 +285,16 @@ public class DcSwitchStateMachine extends StateMachine {
             boolean retVal;
 
             switch (msg.what) {
+                case DcSwitchAsyncChannel.REQ_CONNECT: {
+                    RequestInfo apnRequest = (RequestInfo)msg.obj;
+                    if (DBG) log("DetachingState: REQ_CONNECT, apnRequest=" + apnRequest);
+
+                    // can't process this now - wait until we return to idle
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+                }
+
                 case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
                     if (DBG) {
                         log("DetachingState: EVENT_DATA_DETACHED");
@@ -290,8 +308,6 @@ public class DcSwitchStateMachine extends StateMachine {
                     if (DBG) {
                         log("DetachingState: REQ_DISCONNECT_ALL, already detaching" );
                     }
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
-                            PhoneConstants.APN_REQUEST_STARTED);
                     retVal = HANDLED;
                     break;
                 }
index aea4e12..a6f48b6 100644 (file)
@@ -55,6 +55,7 @@ public class DctController extends Handler {
     private static final int EVENT_EXECUTE_ALL_REQUESTS = 102;
     private static final int EVENT_RELEASE_REQUEST = 103;
     private static final int EVENT_RELEASE_ALL_REQUESTS = 104;
+    private static final int EVENT_RETRY_ATTACH = 105;
 
     private static final int EVENT_DATA_ATTACHED = 500;
     private static final int EVENT_DATA_DETACHED = 600;
@@ -276,6 +277,9 @@ public class DctController extends Handler {
             case EVENT_RELEASE_ALL_REQUESTS:
                 onReleaseAllRequests(msg.arg1);
                 break;
+            case EVENT_RETRY_ATTACH:
+                onRetryAttach(msg.arg1);
+                break;
             default:
                 loge("Un-handled message [" + msg.what + "]");
         }
@@ -327,6 +331,11 @@ public class DctController extends Handler {
         sendMessage(obtainMessage(EVENT_RELEASE_ALL_REQUESTS, phoneId, 0));
     }
 
+    public void retryAttach(int phoneId) {
+        logd("retryAttach, phone:" + phoneId);
+        sendMessage(obtainMessage(EVENT_RETRY_ATTACH, phoneId, 0));
+    }
+
     private void onProcessRequest() {
         //process all requests
         //1. Check all requests and find subscription of the top priority
@@ -354,11 +363,11 @@ public class DctController extends Handler {
             while (iterator.hasNext()) {
                 RequestInfo requestInfo = mRequestInfos.get(iterator.next());
                 if (getRequestPhoneId(requestInfo.request) == phoneId && !requestInfo.executed) {
-                    mDcSwitchAsyncChannel[phoneId].connectSync(requestInfo);
+                    mDcSwitchAsyncChannel[phoneId].connect(requestInfo);
                 }
             }
         } else {
-            mDcSwitchAsyncChannel[activePhoneId].disconnectAllSync();
+            mDcSwitchAsyncChannel[activePhoneId].disconnectAll();
         }
     }
 
@@ -408,6 +417,15 @@ public class DctController extends Handler {
         }
     }
 
+    private void onRetryAttach(int phoneId) {
+        final int topPriPhone = getTopPriorityRequestPhoneId();
+        logd("onRetryAttach phoneId=" + phoneId + " topPri phone = " + topPriPhone);
+
+        if (phoneId != -1 && phoneId == topPriPhone) {
+            mDcSwitchAsyncChannel[phoneId].retryConnect();
+        }
+    }
+
     private void onSettingsChange() {
         //Sub Selection
         long dataSubId = mSubController.getDefaultDataSubId();