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

Commit 233a9f39 authored by Jack Yu's avatar Jack Yu
Browse files

Fixed a race condition for early arrived PCO data

RIL sent PCO data right after setup data call response when
data network has not entered connected state, and caused
PCO data ignored by the data network. Fixed by monitoring
all PCO data as soon as the network enters connecting state.

Test: Manual + Basic testing + atest DataNetworkControllerTest
Fix: 241475706
Merged-In: Id54258d03d63e77866a54e394822d38898ae61a3
Change-Id: Id54258d03d63e77866a54e394822d38898ae61a3
parent 69167e9d
Loading
Loading
Loading
Loading
+55 −35
Original line number Diff line number Diff line
@@ -644,10 +644,10 @@ public class DataNetwork extends StateMachine {
    private @NonNull DataAllowedReason mDataAllowedReason;

    /**
     * PCO (Protocol Configuration Options) data received from the network. Key is the PCO id, value
     * is the PCO content.
     * PCO (Protocol Configuration Options) data received from the network. The first key is the
     * cid of the PCO data, the second key is the PCO id, the value is the PCO data.
     */
    private final @NonNull Map<Integer, PcoData> mPcoData = new ArrayMap<>();
    private final @NonNull Map<Integer, Map<Integer, PcoData>> mPcoData = new ArrayMap<>();

    /** The QOS bearer sessions. */
    private final @NonNull List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
@@ -1009,6 +1009,8 @@ public class DataNetwork extends StateMachine {
                    sendMessage(EVENT_DATA_CONFIG_UPDATED);
                }
            };
            mRil.registerForPcoData(getHandler(), EVENT_PCO_DATA_RECEIVED, null);

            mDataConfigManager.registerCallback(mDataConfigManagerCallback);
            mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
                    getHandler(), EVENT_DISPLAY_INFO_CHANGED, null);
@@ -1061,6 +1063,7 @@ public class DataNetwork extends StateMachine {
            mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
            mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(
                    getHandler());
            mRil.unregisterForPcoData(getHandler());
            mDataConfigManager.unregisterCallback(mDataConfigManagerCallback);
        }

@@ -1108,12 +1111,16 @@ public class DataNetwork extends StateMachine {
                    updateNetworkCapabilities();
                    break;
                }
                case EVENT_PCO_DATA_RECEIVED: {
                    AsyncResult ar = (AsyncResult) msg.obj;
                    onPcoDataReceived((PcoData) ar.result);
                    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:
                case EVENT_STUCK_IN_TRANSIENT_STATE:
                case EVENT_DISPLAY_INFO_CHANGED:
                case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET:
@@ -1193,7 +1200,6 @@ public class DataNetwork extends StateMachine {
                    break;
                case EVENT_NOTIFY_HANDOVER_STARTED:
                case EVENT_TEAR_DOWN_NETWORK:
                case EVENT_PCO_DATA_RECEIVED:
                case EVENT_WAITING_FOR_TEARING_DOWN_CONDITION_MET:
                    // Defer the request until connected or disconnected.
                    log("Defer message " + eventToString(msg.what));
@@ -1260,6 +1266,13 @@ public class DataNetwork extends StateMachine {
                }
            }

            // If we've ever received PCO data before connected, now it's the time to
            // process it.
            mPcoData.getOrDefault(mCid.get(mTransport), Collections.emptyMap())
                    .forEach((pcoId, pcoData) -> {
                        onPcoDataChanged(pcoData);
                    });

            notifyPreciseDataConnectionState();
            updateSuspendState();
        }
@@ -1323,10 +1336,6 @@ public class DataNetwork extends StateMachine {
                case EVENT_SUBSCRIPTION_PLAN_OVERRIDE:
                    updateMeteredAndCongested();
                    break;
                case EVENT_PCO_DATA_RECEIVED:
                    ar = (AsyncResult) msg.obj;
                    onPcoDataReceived((PcoData) ar.result);
                    break;
                case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE:
                    int resultCode = msg.arg1;
                    onDeactivateResponse(resultCode);
@@ -1400,10 +1409,6 @@ public class DataNetwork extends StateMachine {
                    onHandoverResponse(resultCode, dataCallResponse,
                            (DataHandoverRetryEntry) msg.obj);
                    break;
                case EVENT_PCO_DATA_RECEIVED:
                    AsyncResult ar = (AsyncResult) msg.obj;
                    onPcoDataReceived((PcoData) ar.result);
                    break;
                case EVENT_STUCK_IN_TRANSIENT_STATE:
                    // enable detection only for valid timeout range
                    reportAnomaly("Data service did not respond the handover request within "
@@ -1593,7 +1598,6 @@ public class DataNetwork extends StateMachine {
    private void registerForWwanEvents() {
        registerForBandwidthUpdate();
        mKeepaliveTracker.registerForKeepaliveStatus();
        mRil.registerForPcoData(this.getHandler(), EVENT_PCO_DATA_RECEIVED, null);
    }

    /**
@@ -1602,7 +1606,6 @@ public class DataNetwork extends StateMachine {
    private void unregisterForWwanEvents() {
        unregisterForBandwidthUpdate();
        mKeepaliveTracker.unregisterForKeepaliveStatus();
        mRil.unregisterForPcoData(this.getHandler());
    }

    @Override
@@ -3166,8 +3169,6 @@ public class DataNetwork extends StateMachine {
            mDataProfile = mHandoverDataProfile;
            updateDataNetwork(response);
            if (mTransport != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
                // Handover from WWAN to WLAN
                mPcoData.clear();
                unregisterForWwanEvents();
            } else {
                // Handover from WLAN to WWAN
@@ -3202,13 +3203,10 @@ public class DataNetwork extends StateMachine {
    /**
     * Called when receiving PCO (Protocol Configuration Options) data from the cellular network.
     *
     * @param pcoData PCO data.
     * @param pcoData The PCO data.
     */
    private void onPcoDataReceived(@NonNull PcoData pcoData) {
        if (pcoData.cid != getId()) return;
        PcoData oldData = mPcoData.put(pcoData.pcoId, pcoData);
        if (!Objects.equals(oldData, pcoData)) {
            log("onPcoDataReceived: " + pcoData);
    private void onPcoDataChanged(@NonNull PcoData pcoData) {
        log("onPcoDataChanged: " + pcoData);
        mDataNetworkCallback.invokeFromExecutor(
                () -> mDataNetworkCallback.onPcoDataChanged(DataNetwork.this));
        if (mDataProfile.getApnSetting() != null) {
@@ -3223,13 +3221,35 @@ public class DataNetwork extends StateMachine {
            }
        }
    }

    /**
     * Called when receiving PCO (Protocol Configuration Options) data from the cellular network.
     *
     * @param pcoData PCO data.
     */
    private void onPcoDataReceived(@NonNull PcoData pcoData) {
        // Save all the PCO data received, even though it might be unrelated to this data network.
        // The network might be still in connecting state. Save all now and use it when entering
        // connected state.
        log("onPcoDataReceived: " + pcoData);
        PcoData oldData = mPcoData.computeIfAbsent(pcoData.cid, m -> new ArrayMap<>())
                .put(pcoData.pcoId, pcoData);
        if (getId() == INVALID_CID || pcoData.cid != getId()) return;
        if (!Objects.equals(oldData, pcoData)) {
            onPcoDataChanged(pcoData);
        }
    }

    /**
     * @return The PCO data received from the network.
     * @return The PCO data map of the network. The key is the PCO id, the value is the PCO data.
     * An empty map if PCO data is not available.
     */
    public @NonNull Map<Integer, PcoData> getPcoData() {
        return mPcoData;
        if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                || mCid.get(mTransport) == INVALID_CID) {
            return Collections.emptyMap();
        }
        return mPcoData.get(mCid.get(mTransport));
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -3030,7 +3030,7 @@ public class DataNetworkController extends Handler {
            }

            if (nrAdvancedCapableByPco != mNrAdvancedCapableByPco) {
                log("onPcoDataChanged: mNrAdvancedCapableByPco = " + mNrAdvancedCapableByPco);
                log("onPcoDataChanged: mNrAdvancedCapableByPco = " + nrAdvancedCapableByPco);
                mNrAdvancedCapableByPco = nrAdvancedCapableByPco;
                mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                        () -> callback.onNrAdvancedCapableByPcoChanged(mNrAdvancedCapableByPco)));
+56 −0
Original line number Diff line number Diff line
@@ -2374,6 +2374,62 @@ public class DataNetworkControllerTest extends TelephonyTest {
        verify(mMockedDataNetworkControllerCallback).onNrAdvancedCapableByPcoChanged(eq(false));
    }

    @Test
    public void testNrAdvancedByEarlyPco() {
        Mockito.reset(mMockedWwanDataServiceManager);
        mDataNetworkControllerUT.addNetworkRequest(
                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
        processAllMessages();

        // PCO data arrives before data network entering connected state.
        mSimulatedCommands.triggerPcoData(1, "IPV6", 1234, new byte[]{1});
        processAllMessages();

        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
        verify(mMockedWwanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
                anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
                messageCaptor.capture());

        // Send setup data call complete message.
        Message msg = messageCaptor.getValue();
        msg.getData().putParcelable("data_call_response",
                createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE));
        msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
        msg.sendToTarget();
        processAllMessages();

        verify(mMockedDataNetworkControllerCallback).onNrAdvancedCapableByPcoChanged(eq(true));
    }

    @Test
    public void testNrAdvancedByEarlyUnrelatedPco() {
        Mockito.reset(mMockedWwanDataServiceManager);
        mDataNetworkControllerUT.addNetworkRequest(
                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
        processAllMessages();

        // Unrelated PCO data arrives before data network entering connected state.
        mSimulatedCommands.triggerPcoData(2, "IPV6", 1234, new byte[]{1});
        processAllMessages();

        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
        verify(mMockedWwanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
                anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
                messageCaptor.capture());

        // Send setup data call complete message.
        Message msg = messageCaptor.getValue();
        msg.getData().putParcelable("data_call_response",
                createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE));
        msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
        msg.sendToTarget();
        processAllMessages();

        verify(mMockedDataNetworkControllerCallback, never()).onNrAdvancedCapableByPcoChanged(
                anyBoolean());
    }


    @Test
    public void testSetupDataNetworkVcnManaged() throws Exception {
        // VCN managed