Merge "Fixed dangling network agent issue"
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 05bd3e5..78f7779 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -59,6 +59,7 @@
 import android.telephony.data.DataService;
 import android.telephony.data.DataServiceCallback;
 import android.text.TextUtils;
+import android.util.LocalLog;
 import android.util.Pair;
 import android.util.StatsLog;
 import android.util.TimeUtils;
@@ -178,6 +179,8 @@
 
     private final String mTagSuffix;
 
+    private final LocalLog mHandoverLocalLog = new LocalLog(100);
+
     /**
      * Used internally for saving connecting parameters.
      */
@@ -684,6 +687,7 @@
             linkProperties = dc.getLinkProperties();
             // Preserve the potential network agent from the source data connection. The ownership
             // is not transferred at this moment.
+            mHandoverLocalLog.log("Handover started. Preserved the agent.");
             mHandoverSourceNetworkAgent = dc.getNetworkAgent();
             log("Get the handover source network agent: " + mHandoverSourceNetworkAgent);
             dc.setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED);
@@ -1685,6 +1689,31 @@
                 mHandoverState = HANDOVER_STATE_COMPLETED;
             }
 
+            // Check for dangling agent. Ideally the handover source agent should be null if
+            // handover process is smooth. When it's not null, that means handover failed. The
+            // agent was not successfully transferred to the new data connection. We should
+            // gracefully notify connectivity service the network was disconnected.
+            if (mHandoverSourceNetworkAgent != null) {
+                DataConnection sourceDc = mHandoverSourceNetworkAgent.getDataConnection();
+                if (sourceDc != null) {
+                    // If the source data connection still owns this agent, then just reset the
+                    // handover state back to idle because handover is already failed.
+                    mHandoverLocalLog.log(
+                            "Handover failed. Reset the source dc state to idle");
+                    sourceDc.setHandoverState(HANDOVER_STATE_IDLE);
+                } else {
+                    // The agent is now a dangling agent. No data connection owns this agent.
+                    // Gracefully notify connectivity service disconnected.
+                    mHandoverLocalLog.log(
+                            "Handover failed and dangling agent found.");
+                    mHandoverSourceNetworkAgent.acquireOwnership(
+                            DataConnection.this, mTransportType);
+                    mHandoverSourceNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
+                    mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this);
+                }
+                mHandoverSourceNetworkAgent = null;
+            }
+
             if (mConnectionParams != null) {
                 if (DBG) {
                     log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
@@ -1959,7 +1988,9 @@
                 }
 
                 if (mHandoverSourceNetworkAgent != null) {
-                    log("Transfer network agent successfully.");
+                    String logStr = "Transfer network agent successfully.";
+                    log(logStr);
+                    mHandoverLocalLog.log(logStr);
                     mNetworkAgent = mHandoverSourceNetworkAgent;
                     mNetworkAgent.acquireOwnership(DataConnection.this, mTransportType);
 
@@ -1973,7 +2004,9 @@
                     mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
                     mHandoverSourceNetworkAgent = null;
                 } else {
-                    loge("Failed to get network agent from original data connection");
+                    String logStr = "Failed to get network agent from original data connection";
+                    loge(logStr);
+                    mHandoverLocalLog.log(logStr);
                     return;
                 }
             } else {
@@ -2589,6 +2622,8 @@
     }
 
     void setHandoverState(@HandoverState int state) {
+        mHandoverLocalLog.log("State changed from " + handoverStateToString(mHandoverState)
+                + " to " + handoverStateToString(state));
         mHandoverState = state;
     }
 
@@ -2812,6 +2847,15 @@
         return score;
     }
 
+    private String handoverStateToString(@HandoverState int state) {
+        switch (state) {
+            case HANDOVER_STATE_IDLE: return "IDLE";
+            case HANDOVER_STATE_BEING_TRANSFERRED: return "BEING_TRANSFERRED";
+            case HANDOVER_STATE_COMPLETED: return "COMPLETED";
+            default: return "UNKNOWN";
+        }
+    }
+
     /**
      * Dump the current state.
      *
@@ -2841,7 +2885,7 @@
         pw.println("mLinkProperties=" + mLinkProperties);
         pw.flush();
         pw.println("mDataRegState=" + mDataRegState);
-        pw.println("mHandoverState=" + mHandoverState);
+        pw.println("mHandoverState=" + handoverStateToString(mHandoverState));
         pw.println("mRilRat=" + mRilRat);
         pw.println("mNetworkCapabilities=" + getNetworkCapabilities());
         pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
@@ -2859,6 +2903,10 @@
         if (mNetworkAgent != null) {
             mNetworkAgent.dump(fd, pw, args);
         }
+        pw.println("handover local log:");
+        pw.increaseIndent();
+        mHandoverLocalLog.dump(fd, pw, args);
+        pw.decreaseIndent();
         pw.decreaseIndent();
         pw.println();
         pw.flush();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
index b36a490..d9edd28 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
@@ -132,6 +132,13 @@
         mDataConnection = null;
     }
 
+    /**
+     * @return The data connection that owns this agent
+     */
+    public synchronized DataConnection getDataConnection() {
+        return mDataConnection;
+    }
+
     @Override
     protected synchronized void unwanted() {
         if (mDataConnection == null) {