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

Commit f4a4b3f3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Cancel pending retry upon data profile unthrottle" am: c7248db5

parents d5dcd309 c7248db5
Loading
Loading
Loading
Loading
+58 −11
Original line number Diff line number Diff line
@@ -969,18 +969,16 @@ public class DataRetryManager extends Handler {
        switch (msg.what) {
            case EVENT_DATA_SETUP_RETRY:
                DataSetupRetryEntry dataSetupRetryEntry = (DataSetupRetryEntry) msg.obj;
                Objects.requireNonNull(dataSetupRetryEntry);
                if (!isRetryCancelled(dataSetupRetryEntry)) {
                    mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                            () -> callback.onDataNetworkSetupRetry(dataSetupRetryEntry)));
                }
                break;
            case EVENT_DATA_HANDOVER_RETRY:
                DataHandoverRetryEntry dataHandoverRetryEntry = (DataHandoverRetryEntry) msg.obj;
                Objects.requireNonNull(dataHandoverRetryEntry);
                if (mDataRetryEntries.contains(dataHandoverRetryEntry)) {
                if (!isRetryCancelled(dataHandoverRetryEntry)) {
                    mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                            () -> callback.onDataNetworkHandoverRetry(dataHandoverRetryEntry)));
                } else {
                    log("Handover was cancelled earlier. " + dataHandoverRetryEntry);
                }
                break;
            case EVENT_RADIO_ON:
@@ -1013,6 +1011,18 @@ public class DataRetryManager extends Handler {
        }
    }

    /**
     * @param retryEntry The retry entry to check.
     * @return {@code true} if the retry is null or not in RETRY_STATE_NOT_RETRIED state.
     */
    private boolean isRetryCancelled(@Nullable DataRetryEntry retryEntry) {
        if (retryEntry != null && retryEntry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) {
            return false;
        }
        log("Retry was removed earlier. " + retryEntry);
        return true;
    }

    /**
     * Called when carrier config is updated.
     */
@@ -1385,7 +1395,7 @@ public class DataRetryManager extends Handler {
     * @param remove Whether to remove unthrottled entries from the list of entries.
     */
    private void onDataProfileUnthrottled(@Nullable DataProfile dataProfile, @Nullable String apn,
            int transport, boolean remove) {
            @TransportType int transport, boolean remove) {
        log("onDataProfileUnthrottled: data profile=" + dataProfile + ", apn=" + apn
                + ", transport=" + AccessNetworkConstants.transportTypeToString(transport)
                + ", remove=" + remove);
@@ -1456,11 +1466,16 @@ public class DataRetryManager extends Handler {
        mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                () -> callback.onThrottleStatusChanged(throttleStatusList)));

        if (unthrottledProfile != null) {
            // cancel pending retries since we will soon schedule an immediate retry
            cancelRetriesForDataProfile(unthrottledProfile, transport);
        }

        logl("onDataProfileUnthrottled: Removing the following throttling entries. "
                + dataUnthrottlingEntries);
        for (DataThrottlingEntry entry : dataUnthrottlingEntries) {
            if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) {
            // Immediately retry after unthrottling.
            if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) {
                schedule(new DataSetupRetryEntry.Builder<>()
                        .setDataProfile(entry.dataProfile)
                        .setTransport(entry.transport)
@@ -1480,6 +1495,34 @@ public class DataRetryManager extends Handler {
        }
    }

    /**
     * Cancel pending retries that uses the specified data profile, with specified target transport.
     *
     * @param dataProfile The data profile to cancel.
     * @param transport The target {@link TransportType} on which the retry to cancel.
     */
    private void cancelRetriesForDataProfile(@NonNull DataProfile dataProfile,
            @TransportType int transport) {
        logl("cancelRetriesForDataProfile: Canceling pending retries for " + dataProfile);
        mDataRetryEntries.stream()
                .filter(entry -> {
                    if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) {
                        if (entry instanceof DataSetupRetryEntry) {
                            DataSetupRetryEntry retryEntry = (DataSetupRetryEntry) entry;
                            return dataProfile.equals(retryEntry.dataProfile)
                                    && transport == retryEntry.transport;
                        } else if (entry instanceof DataHandoverRetryEntry) {
                            DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) entry;
                            return dataProfile.equals(retryEntry.dataNetwork.getDataProfile());
                        }
                    }
                    return false;
                })
                .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED));
    }



    /**
     * Check if there is any similar network request scheduled to retry. The definition of similar
     * is that network requests have same APN capability and on the same transport.
@@ -1564,14 +1607,18 @@ public class DataRetryManager extends Handler {
     * @param dataNetwork The data network that was originally scheduled for handover retry.
     */
    private void onCancelPendingHandoverRetry(@NonNull DataNetwork dataNetwork) {
        mDataRetryEntries.removeIf(entry -> entry instanceof DataHandoverRetryEntry
                && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork);
        mDataRetryEntries.stream()
                .filter(entry -> entry instanceof DataHandoverRetryEntry
                        && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork
                        && entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED)
                .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED));
        mDataThrottlingEntries.removeIf(entry -> entry.dataNetwork == dataNetwork);
    }

    /**
     * Check if there is any data handover retry scheduled.
     *
     *
     * @param dataNetwork The network network to retry handover.
     * @return {@code true} if there is retry scheduled for this network capability.
     */
+93 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.internal.telephony.data;

import static com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry;
import static com.android.internal.telephony.data.DataRetryManager.DataRetryEntry;
import static com.android.internal.telephony.data.DataRetryManager.DataSetupRetryEntry;

import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.net.NetworkCapabilities;
@@ -54,6 +57,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;

@@ -323,15 +327,38 @@ public class DataRetryManagerTest extends TelephonyTest {
    }

    @Test
    public void testDataSetupUnthrottling() {
    public void testDataSetupUnthrottling() throws Exception {
        testDataSetupRetryNetworkSuggestedNeverRetry();
        Mockito.clearInvocations(mDataRetryManagerCallbackMock);
        DataNetworkController.NetworkRequestList mockNrl = Mockito.mock(
                DataNetworkController.NetworkRequestList.class);
        Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
        field.setAccessible(true);
        List<DataRetryEntry> mDataRetryEntries =
                (List<DataRetryEntry>) field.get(mDataRetryManagerUT);

        // schedule 2 setup retries
        DataSetupRetryEntry scheduledRetry1 = new DataSetupRetryEntry.Builder<>()
                .setDataProfile(mDataProfile3)
                .setNetworkRequestList(mockNrl)
                .setTransport(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                .setSetupRetryType(1)
                .build();
        DataSetupRetryEntry scheduledRetry2 = new DataSetupRetryEntry.Builder<>()
                .setNetworkRequestList(mockNrl)
                .setDataProfile(mDataProfile3)
                .setTransport(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
                .setSetupRetryType(1)
                .build();
        mDataRetryEntries.addAll(List.of(scheduledRetry1, scheduledRetry2));

        // unthrottle the data profile, expect previous retries of the same transport is cancelled
        mDataRetryManagerUT.obtainMessage(6/*EVENT_DATA_PROFILE_UNTHROTTLED*/,
                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, mDataProfile3, null))
                .sendToTarget();
        processAllMessages();

        // check unthrottle
        ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
                ArgumentCaptor.forClass(List.class);
        verify(mDataRetryManagerCallbackMock).onThrottleStatusChanged(
@@ -353,6 +380,10 @@ public class DataRetryManagerTest extends TelephonyTest {
        assertThat(entry.dataProfile).isEqualTo(mDataProfile3);
        assertThat(entry.retryDelayMillis).isEqualTo(0);
        assertThat(entry.transport).isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);

        // check mDataProfile3-WWAN retry is cancelled, but not the WLAN
        assertThat(scheduledRetry1.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
        assertThat(scheduledRetry2.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
    }

    @Test
@@ -390,6 +421,66 @@ public class DataRetryManagerTest extends TelephonyTest {
        assertThat(entry.transport).isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
    }

    @Test
    public void testCancellingRetries() throws Exception {
        DataNetworkController.NetworkRequestList mockNrl = Mockito.mock(
                DataNetworkController.NetworkRequestList.class);

        // Test: setup retry
        DataRetryEntry retry = new DataSetupRetryEntry.Builder<>()
                .setSetupRetryType(1)
                .setNetworkRequestList(mockNrl)
                .setTransport(1)
                .build();
        retry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);

        mDataRetryManagerUT.obtainMessage(3/*EVENT_DATA_SETUP_RETRY*/, retry).sendToTarget();
        processAllMessages();
        verify(mDataRetryManagerCallbackMock, never()).onDataNetworkSetupRetry(any());

        mDataRetryManagerUT.obtainMessage(3/*EVENT_DATA_SETUP_RETRY*/, null).sendToTarget();
        processAllMessages();
        verify(mDataRetryManagerCallbackMock, never()).onDataNetworkSetupRetry(any());

        retry.setState(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
        mDataRetryManagerUT.obtainMessage(3/*EVENT_DATA_SETUP_RETRY*/, retry).sendToTarget();
        processAllMessages();
        verify(mDataRetryManagerCallbackMock, times(1)).onDataNetworkSetupRetry(any());

        // Test: handover retry
        retry = new DataHandoverRetryEntry.Builder<>().build();
        retry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
        mDataRetryManagerUT.obtainMessage(4/*EVENT_DATA_HANDOVER_RETRY*/, retry).sendToTarget();
        processAllMessages();
        verify(mDataRetryManagerCallbackMock, never()).onDataNetworkHandoverRetry(any());

        mDataRetryManagerUT.obtainMessage(4/*EVENT_DATA_HANDOVER_RETRY*/, null).sendToTarget();
        processAllMessages();
        verify(mDataRetryManagerCallbackMock, never()).onDataNetworkHandoverRetry(any());

        retry.setState(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
        mDataRetryManagerUT.obtainMessage(4/*EVENT_DATA_HANDOVER_RETRY*/, retry).sendToTarget();
        processAllMessages();
        verify(mDataRetryManagerCallbackMock, times(1))
                .onDataNetworkHandoverRetry(any());

        // Test: cancelPendingHandoverRetry
        DataNetwork mockDn = Mockito.mock(DataNetwork.class);
        Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
        field.setAccessible(true);
        List<DataRetryEntry> mDataRetryEntries =
                (List<DataRetryEntry>) field.get(mDataRetryManagerUT);
        retry = new DataHandoverRetryEntry.Builder<>()
                .setDataNetwork(mockDn)
                .build();
        mDataRetryEntries.add(retry);
        mDataRetryManagerUT.cancelPendingHandoverRetry(mockDn);
        processAllMessages();

        assertThat(mDataRetryManagerUT.isAnyHandoverRetryScheduled(mockDn)).isFalse();
        assertThat(retry.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
    }

    @Test
    public void testDataSetupRetryPermanentFailure() {
        DataSetupRetryRule retryRule = new DataSetupRetryRule(
@@ -584,7 +675,7 @@ public class DataRetryManagerTest extends TelephonyTest {
            assertThat(entry.networkRequestList).isEqualTo(networkRequestList);
            assertThat(entry.appliedDataRetryRule).isEqualTo(retryRule3);

            entry.setState(DataRetryManager.DataRetryEntry.RETRY_STATE_FAILED);
            entry.setState(DataRetryEntry.RETRY_STATE_FAILED);
        }

        // The last fail should not trigger any retry.