Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa3711eb authored by Jack Yu's avatar Jack Yu
Browse files

Notify source transport for upcoming handover

When handover is about to happen, notify the source transport
that handover will be performed on the target transport. After
startHandover is called for certain PDN, modem should preserve
the PDU session id for this PDN even if network releases it,
because the PDU session id will be continuously used on the PDN
on target transport.

In the rare scenario that PDU session id is preserved due to
startHandover is called but handover also failed on target
transport, modem should release the PDU session id when
cancelHandover is called. In the normal scenario, if a PDN is
disconnected on IWLAN, frameworks will call releasePduSessionId
so the PDU session can be released back into the pool.

Fix: 235509108
Test: Manual handover testing + DataNetworkControllerTest
      & DataNetworkTest
Merged-In: Ifc59fca55b1f35233f613ecaac4fcb376204fc29
Change-Id: Ifc59fca55b1f35233f613ecaac4fcb376204fc29
parent a0c367bf
Loading
Loading
Loading
Loading
+68 −14
Original line number Diff line number Diff line
@@ -193,9 +193,6 @@ public class DataNetwork extends StateMachine {
    /** Event for display info changed. This is for getting 5G NSA or mmwave information. */
    private static final int EVENT_DISPLAY_INFO_CHANGED = 13;

    /** Event for initiating an handover between cellular and IWLAN. */
    private static final int EVENT_START_HANDOVER = 14;

    /** Event for setup data call (for handover) response from the data service. */
    private static final int EVENT_HANDOVER_RESPONSE = 15;

@@ -235,6 +232,24 @@ public class DataNetwork extends StateMachine {
    /** Event for CSS indicator changed. */
    private static final int EVENT_CSS_INDICATOR_CHANGED = 24;

    /**
     * Event for notifying source transport that handover is about to be initiated on target
     * transport.
     */
    private static final int EVENT_NOTIFY_HANDOVER_STARTED = 25;

    /**
     * Event for the response of notifying source transport that handover is about to be initiated
     * on target transport.
     */
    private static final int EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE = 26;

    /**
     * Event for the response of notifying source transport that handover is cancelled/failed on the
     * target transport.
     */
    private static final int EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE = 27;

    /** Invalid context id. */
    private static final int INVALID_CID = -1;

@@ -1096,6 +1111,9 @@ public class DataNetwork extends StateMachine {
                    updateNetworkCapabilities();
                    break;
                }
                case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE:
                    log("Notified handover cancelled.");
                    break;
                case EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED:
                case EVENT_TEAR_DOWN_NETWORK:
                case EVENT_PCO_DATA_RECEIVED:
@@ -1108,15 +1126,16 @@ public class DataNetwork extends StateMachine {
                    // Ignore the events when not in the correct state.
                    log("Ignored " + eventToString(msg.what));
                    break;
                case EVENT_START_HANDOVER:
                    // We reach here if network is not in the connected/connecting state.
                case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE:
                case EVENT_NOTIFY_HANDOVER_STARTED:
                    // We reach here if network is not in the right state.
                    if (msg.obj != null) {
                        // Cancel it because it's either HO in progress or will soon disconnect.
                        // Either case we want to clean up obsolete retry attempts.
                        DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) msg.obj;
                        retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
                    }
                    log("Ignore retry for the handover to " + AccessNetworkConstants
                    log("Ignore handover to " + AccessNetworkConstants
                            .transportTypeToString(msg.arg1) + " request.");
                    break;
                case EVENT_RADIO_NOT_AVAILABLE:
@@ -1187,7 +1206,7 @@ public class DataNetwork extends StateMachine {
                            msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
                    onSetupResponse(resultCode, dataCallResponse);
                    break;
                case EVENT_START_HANDOVER:
                case EVENT_NOTIFY_HANDOVER_STARTED:
                case EVENT_TEAR_DOWN_NETWORK:
                case EVENT_PCO_DATA_RECEIVED:
                case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET:
@@ -1300,8 +1319,21 @@ public class DataNetwork extends StateMachine {
                case EVENT_DISPLAY_INFO_CHANGED:
                    onDisplayInfoChanged();
                    break;
                case EVENT_START_HANDOVER:
                    onStartHandover(msg.arg1, (DataHandoverRetryEntry) msg.obj);
                case EVENT_NOTIFY_HANDOVER_STARTED:
                    // Notify source transport that handover is about to start. Note this will not
                    // initiate the handover process on target transport, but more for notifying
                    // the source transport so that PDU session id can be preserved if network
                    // notifies PDN lost during handover. The real handover process will kick off
                    // after receiving EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE.
                    log("Notifying source transport "
                            + AccessNetworkConstants.transportTypeToString(mTransport)
                            + " that handover is about to start.");
                    mDataServiceManagers.get(mTransport).startHandover(mCid.get(mTransport),
                            obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE, 0, msg.arg2,
                                    msg.obj));
                    // We enter handover state here because this is the first action we do for
                    // handover.
                    transitionTo(mHandoverState);
                    break;
                case EVENT_SUBSCRIPTION_PLAN_OVERRIDE:
                    updateMeteredAndCongested();
@@ -1373,6 +1405,9 @@ public class DataNetwork extends StateMachine {
                    log("Defer message " + eventToString(msg.what));
                    deferMessage(msg);
                    break;
                case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE:
                    onStartHandover(msg.arg2, (DataHandoverRetryEntry) msg.obj);
                    break;
                case EVENT_HANDOVER_RESPONSE:
                    int resultCode = msg.arg1;
                    DataCallResponse dataCallResponse =
@@ -3036,6 +3071,12 @@ public class DataNetwork extends StateMachine {
    /**
     * Request the data network to handover to the target transport.
     *
     * This is the starting point of initiating IWLAN/cellular handover. It will first call
     * {@link DataServiceManager#startHandover(int, Message)} to notify source transport that
     * handover is about to start, and then call {@link DataServiceManager#setupDataCall(int,
     * DataProfile, boolean, boolean, int, LinkProperties, int, NetworkSliceInfo, TrafficDescriptor,
     * boolean, Message)} on target transport to initiate the handover process.
     *
     * @param targetTransport The target transport.
     * @param retryEntry Data handover retry entry. This would be {@code null} for first time
     * handover attempt.
@@ -3048,12 +3089,16 @@ public class DataNetwork extends StateMachine {
            if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
            return false;
        }
        sendMessage(obtainMessage(EVENT_START_HANDOVER, targetTransport, 0, retryEntry));

        // Before we really initiate the handover process on target transport, we need to notify
        // source transport that handover is about to start. Handover will be eventually initiated
        // in onStartHandover().
        sendMessage(obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED, 0, targetTransport, retryEntry));
        return true;
    }

    /**
     * Called when handover between IWLAN and cellular is needed.
     * Called when starting IWLAN/cellular handover process on the target transport.
     *
     * @param targetTransport The target transport.
     * @param retryEntry Data handover retry entry. This would be {@code null} for first time
@@ -3108,7 +3153,6 @@ public class DataNetwork extends StateMachine {
                DataService.REQUEST_REASON_HANDOVER, mLinkProperties, mPduSessionId,
                mNetworkSliceInfo, mHandoverDataProfile.getTrafficDescriptor(), true,
                obtainMessage(EVENT_HANDOVER_RESPONSE, retryEntry));
        transitionTo(mHandoverState);
    }

    /**
@@ -3153,6 +3197,12 @@ public class DataNetwork extends StateMachine {
                    () -> mDataNetworkCallback.onHandoverSucceeded(DataNetwork.this));
        } else {
            // Handover failed.

            // Notify source transport that handover failed on target transport so that PDU session
            // id can be released if it is preserved for handover.
            mDataServiceManagers.get(mTransport).cancelHandover(mCid.get(mTransport),
                    obtainMessage(EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE));

            long retry = response != null ? response.getRetryDurationMillis()
                    : DataCallResponse.RETRY_DURATION_UNDEFINED;
            // If the handover mode is unspecified, default to HANDOVER_FAILURE_MODE_UNKNOWN,
@@ -3331,8 +3381,6 @@ public class DataNetwork extends StateMachine {
                return "EVENT_BANDWIDTH_ESTIMATE_FROM_MODEM_CHANGED";
            case EVENT_DISPLAY_INFO_CHANGED:
                return "EVENT_DISPLAY_INFO_CHANGED";
            case EVENT_START_HANDOVER:
                return "EVENT_START_HANDOVER";
            case EVENT_HANDOVER_RESPONSE:
                return "EVENT_HANDOVER_RESPONSE";
            case EVENT_SUBSCRIPTION_PLAN_OVERRIDE:
@@ -3353,6 +3401,12 @@ public class DataNetwork extends StateMachine {
                return "EVENT_VOICE_CALL_ENDED";
            case EVENT_CSS_INDICATOR_CHANGED:
                return "EVENT_CSS_INDICATOR_CHANGED";
            case EVENT_NOTIFY_HANDOVER_STARTED:
                return "EVENT_NOTIFY_HANDOVER_STARTED";
            case EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE:
                return "EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE";
            case EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE:
                return "EVENT_NOTIFY_HANDOVER_CANCELLED_RESPONSE";
            default:
                return "Unknown(" + event + ")";
        }
+14 −0
Original line number Diff line number Diff line
@@ -700,6 +700,20 @@ public class DataNetworkControllerTest extends TelephonyTest {
                return null;
            }).when(mMockedDataServiceManagers.get(transport)).registerForDataCallListChanged(any(
                    Handler.class), anyInt());

            doAnswer(invocation -> {
                Message msg = (Message) invocation.getArguments()[1];
                msg.sendToTarget();
                return null;
            }).when(mMockedDataServiceManagers.get(transport)).startHandover(anyInt(),
                    any(Message.class));

            doAnswer(invocation -> {
                Message msg = (Message) invocation.getArguments()[1];
                msg.sendToTarget();
                return null;
            }).when(mMockedDataServiceManagers.get(transport)).cancelHandover(anyInt(),
                    any(Message.class));
        }

        doReturn(-1).when(mPhone).getSubId();
+23 −1
Original line number Diff line number Diff line
@@ -313,6 +313,24 @@ public class DataNetworkTest extends TelephonyTest {
                mMockedWwanDataServiceManager);
        mDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
                mMockedWlanDataServiceManager);

        for (int transport : new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                AccessNetworkConstants.TRANSPORT_TYPE_WLAN}) {
            doAnswer(invocation -> {
                Message msg = (Message) invocation.getArguments()[1];
                msg.sendToTarget();
                return null;
            }).when(mDataServiceManagers.get(transport)).startHandover(anyInt(),
                    any(Message.class));

            doAnswer(invocation -> {
                Message msg = (Message) invocation.getArguments()[1];
                msg.sendToTarget();
                return null;
            }).when(mDataServiceManagers.get(transport)).cancelHandover(anyInt(),
                    any(Message.class));
        }

        doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
                .getPreferredTransportByNetworkCapability(anyInt());
@@ -876,6 +894,7 @@ public class DataNetworkTest extends TelephonyTest {
        mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
        processAllMessages();

        verify(mMockedWwanDataServiceManager).startHandover(eq(123), any(Message.class));
        verify(mLinkBandwidthEstimator).unregisterCallback(any(
                LinkBandwidthEstimatorCallback.class));
        assertThat(mDataNetworkUT.getTransport())
@@ -899,8 +918,9 @@ public class DataNetworkTest extends TelephonyTest {
        Mockito.clearInvocations(mDataNetworkCallback);
        Mockito.clearInvocations(mLinkBandwidthEstimator);
        mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, null);
        processAllMessages();
        processAllFutureMessages();

        verify(mMockedWlanDataServiceManager).startHandover(eq(456), any(Message.class));
        verify(mLinkBandwidthEstimator).registerCallback(
                any(LinkBandwidthEstimatorCallback.class));
        assertThat(mDataNetworkUT.getTransport())
@@ -919,6 +939,8 @@ public class DataNetworkTest extends TelephonyTest {
        mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
        processAllMessages();

        verify(mMockedWwanDataServiceManager).startHandover(eq(123), any(Message.class));
        verify(mMockedWwanDataServiceManager).cancelHandover(eq(123), any(Message.class));
        verify(mDataNetworkCallback).onHandoverFailed(eq(mDataNetworkUT),
                eq(DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE), eq(-1L),
                eq(DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN));