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

Commit 29f8316b authored by Thomas Nguyen's avatar Thomas Nguyen
Browse files

Set radio power state to OFF only after getting the state changed event from modem

Problem scenario:
1. APM is enabled
2. SatelliteController get the notification onCellularRadioPowerOffRequested
3. SatelliteController set its radio power state to OFF
4. Modem fails to power off the modem
5. APM is disabled. Since radio state is still on, no update for radio power state from modem is sent.
6. SatelliteController still think radio power is OFF
7. SatelliteController rejects the requests to enable satellite

Flag: EXEMPT bugfix
Bug: 355242339
Test: manual test with Skylo live network
atest SatelliteControllerTest
atest ServiceStateTrackerTest
Test: 356653075

Change-Id: I2595afef393c735a2bb474e6fb0bf930b1238d17
parent c976b5a2
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -1164,6 +1164,9 @@ public class ServiceStateTracker extends Handler {

        mDesiredPowerState = power;
        setPowerStateToDesired(forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply);
        if (mDesiredPowerState) {
            SatelliteController.getInstance().onSetCellularRadioPowerStateRequested(true);
        }
    }

    /**
@@ -1325,6 +1328,12 @@ public class ServiceStateTracker extends Handler {
                    // Hence, issuing shut down regardless of radio power response
                    mCi.requestShutdown(null);
                }

                ar = (AsyncResult) msg.obj;
                if (ar.exception != null) {
                    loge("EVENT_RADIO_POWER_OFF_DONE: exception=" + ar.exception);
                    SatelliteController.getInstance().onPowerOffCellularRadioFailed();
                }
                break;

            // GSM
@@ -4979,7 +4988,7 @@ public class ServiceStateTracker extends Handler {
     */
    public void powerOffRadioSafely() {
        synchronized (this) {
            SatelliteController.getInstance().onCellularRadioPowerOffRequested();
            SatelliteController.getInstance().onSetCellularRadioPowerStateRequested(false);
            if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
                EmergencyStateTracker.getInstance().onCellularRadioPowerOffRequested();
            }
+91 −16
Original line number Diff line number Diff line
@@ -240,6 +240,7 @@ public class SatelliteController extends Handler {
    private static final int EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT = 46;
    private static final int EVENT_WIFI_CONNECTIVITY_STATE_CHANGED = 47;
    private static final int EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT = 48;
    protected static final int EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT = 49;

    @NonNull private static SatelliteController sInstance;
    @NonNull private final Context mContext;
@@ -343,9 +344,13 @@ public class SatelliteController extends Handler {
    private final Object mIsSatelliteEnabledLock = new Object();
    @GuardedBy("mIsSatelliteEnabledLock")
    private Boolean mIsSatelliteEnabled = null;
    private final Object mIsRadioOnLock = new Object();
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    protected final Object mIsRadioOnLock = new Object();
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    protected boolean mIsRadioOn;
    @GuardedBy("mIsRadioOnLock")
    private boolean mIsRadioOn = false;
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    protected boolean mRadioOffRequested = false;
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    protected final Object mSatelliteViaOemProvisionLock = new Object();
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -1324,6 +1329,14 @@ public class SatelliteController extends Handler {
                        mIsRadioOn = true;
                    } else if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
                        resetCarrierRoamingSatelliteModeParams();
                        synchronized (mIsRadioOnLock) {
                            if (mRadioOffRequested) {
                                logd("EVENT_RADIO_STATE_CHANGED: set mIsRadioOn to false");
                                stopWaitForCellularModemOffTimer();
                                mIsRadioOn = false;
                                mRadioOffRequested = false;
                            }
                        }
                    }
                }

@@ -1634,6 +1647,13 @@ public class SatelliteController extends Handler {
                break;
            }

            case EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT: {
                plogw("Timed out to wait for cellular modem OFF state");
                synchronized (mIsRadioOnLock) {
                    mRadioOffRequested = false;
                }
            }

            default:
                Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                        msg.what);
@@ -1715,6 +1735,12 @@ public class SatelliteController extends Handler {
                            SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE, result);
                    return;
                }
                if (mRadioOffRequested) {
                    ploge("Radio is being powering off, can not enable satellite");
                    sendErrorAndReportSessionMetrics(
                            SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE, result);
                    return;
                }
            }

            if (mTelecomManager.isInEmergencyCall()) {
@@ -2899,27 +2925,50 @@ public class SatelliteController extends Handler {

    /**
     * This function is used by {@link com.android.internal.telephony.ServiceStateTracker} to notify
     * {@link SatelliteController} that it has received a request to power off the cellular radio
     * modem. {@link SatelliteController} will then power off the satellite modem.
     * {@link SatelliteController} that it has received a request to power on or off the cellular
     * radio modem.
     *
     * @param powerOn {@code true} means cellular radio is about to be powered on, {@code false}
     *                 means cellular modem is about to be powered off.
     */
    public void onCellularRadioPowerOffRequested() {
        logd("onCellularRadioPowerOffRequested()");
    public void onSetCellularRadioPowerStateRequested(boolean powerOn) {
        logd("onSetCellularRadioPowerStateRequested: powerOn=" + powerOn);
        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
            plogd("onCellularRadioPowerOffRequested: oemEnabledSatelliteFlag is disabled");
            plogd("onSetCellularRadioPowerStateRequested: oemEnabledSatelliteFlag is disabled");
            return;
        }

        synchronized (mIsRadioOnLock) {
            mIsRadioOn = false;
            mRadioOffRequested = !powerOn;
        }
        if (powerOn) {
            stopWaitForCellularModemOffTimer();
        } else {
            requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */,
                    false /* enableSatellite */, false /* enableDemoMode */,
                    false /* isEmergency */,
                    new IIntegerConsumer.Stub() {
                        @Override
                        public void accept(int result) {
                        plogd("onRadioPowerOffRequested: requestSatelliteEnabled result=" + result);
                            plogd("onSetCellularRadioPowerStateRequested: requestSatelliteEnabled"
                                    + " result=" + result);
                        }
                    });
            startWaitForCellularModemOffTimer();
        }
    }

    /**
     * This function is used by {@link com.android.internal.telephony.ServiceStateTracker} to notify
     * {@link SatelliteController} that the request to power off the cellular radio modem has
     * failed.
     */
    public void onPowerOffCellularRadioFailed() {
        logd("onPowerOffCellularRadioFailed");
        synchronized (mIsRadioOnLock) {
            mRadioOffRequested = false;
            stopWaitForCellularModemOffTimer();
        }
    }

    /**
@@ -4954,6 +5003,32 @@ public class SatelliteController extends Handler {
                R.integer.config_wait_for_satellite_enabling_response_timeout_millis);
    }

    private long getWaitForCellularModemOffTimeoutMillis() {
        return mContext.getResources().getInteger(
                R.integer.config_satellite_wait_for_cellular_modem_off_timeout_millis);
    }

    private void startWaitForCellularModemOffTimer() {
        synchronized (mIsRadioOnLock) {
            if (hasMessages(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT)) {
                plogd("startWaitForCellularModemOffTimer: the timer was already started");
                return;
            }
            long timeoutMillis = getWaitForCellularModemOffTimeoutMillis();
            plogd("Start timer to wait for cellular modem OFF state, timeoutMillis="
                    + timeoutMillis);
            sendMessageDelayed(obtainMessage(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT),
                    timeoutMillis);
        }
    }

    private void stopWaitForCellularModemOffTimer() {
        synchronized (mSatelliteEnabledRequestLock) {
            plogd("Stop timer to wait for cellular modem OFF state");
            removeMessages(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT);
        }
    }

    private void startWaitForSatelliteEnablingResponseTimer(
            @NonNull RequestSatelliteEnabledArgument argument) {
        synchronized (mSatelliteEnabledRequestLock) {
+6 −3
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.nullable;
@@ -594,7 +595,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
        sst.setRadioPowerForReason(false, false, false, false, reason);
        assertTrue(sst.getRadioPowerOffReasons().contains(reason));
        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
        verify(mSatelliteController).onCellularRadioPowerOffRequested();
        verify(mSatelliteController).onSetCellularRadioPowerStateRequested(eq(false));
        clearInvocations(mSatelliteController);
        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
@@ -602,7 +603,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
                TelephonyManager.RADIO_POWER_REASON_USER);
        assertTrue(sst.getRadioPowerOffReasons().contains(reason));
        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
        verify(mSatelliteController, never()).onCellularRadioPowerOffRequested();
        verify(mSatelliteController, never()).onSetCellularRadioPowerStateRequested(anyBoolean());
        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);

@@ -610,7 +611,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
        // had been turned off for.
        sst.setRadioPowerForReason(true, false, false, false, reason);
        assertTrue(sst.getRadioPowerOffReasons().isEmpty());
        verify(mSatelliteController, never()).onCellularRadioPowerOffRequested();
        verify(mSatelliteController).onSetCellularRadioPowerStateRequested(eq(true));
        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);

@@ -1928,6 +1929,8 @@ public class ServiceStateTrackerTest extends TelephonyTest {
        sst.setRadioPower(false);
        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
        verify(mSatelliteController).onSetCellularRadioPowerStateRequested(eq(false));
        verify(mSatelliteController).onPowerOffCellularRadioFailed();
        sst.requestShutdown();
        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
        assertFalse(mSimulatedCommands.getRadioState()
+141 −2
Original line number Diff line number Diff line
@@ -183,6 +183,8 @@ public class SatelliteControllerTest extends TelephonyTest {
    private static final int[] ACTIVE_SUB_IDS = {SUB_ID};
    private static final int TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS =
            (int) TimeUnit.SECONDS.toMillis(60);
    private static final int TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS =
            (int) TimeUnit.SECONDS.toMillis(60);

    private static final String SATELLITE_PLMN = "00103";
    private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
@@ -517,6 +519,9 @@ public class SatelliteControllerTest extends TelephonyTest {
        mContextFixture.putIntResource(
                R.integer.config_wait_for_satellite_enabling_response_timeout_millis,
                TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS);
        mContextFixture.putIntResource(
                R.integer.config_satellite_wait_for_cellular_modem_off_timeout_millis,
                TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS);
        doReturn(ACTIVE_SUB_IDS).when(mMockSubscriptionManagerService).getActiveSubIdList(true);

        mCarrierConfigBundle = mContextFixture.getCarrierConfigBundle();
@@ -727,6 +732,87 @@ public class SatelliteControllerTest extends TelephonyTest {
        processAllMessages();
        verify(mMockSatelliteModemInterface, times(5))
                .requestIsSatelliteSupported(any(Message.class));
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // Radio is off during TN -> NTN image switch, SatelliteController should not set radio
        // state to OFF
        setRadioPower(false);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // Turn on radio
        setRadioPower(true);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // APM is triggered
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // SatelliteController should set the radio state to OFF
        setRadioPower(false);
        processAllMessages();
        assertFalse(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // Turn on radio
        setRadioPower(true);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // APM is triggered
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // Modem fails to power off radio. APM is disabled
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(true);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // APM is triggered
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // The timer WaitForCellularModemOff time out
        moveTimeForward(TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // APM is triggered
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());

        // Modem failed to power off the radio
        mSatelliteControllerUT.onPowerOffCellularRadioFailed();
        processAllMessages();
        assertTrue(mSatelliteControllerUT.isRadioOn());
        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
    }

    @Test
@@ -847,7 +933,7 @@ public class SatelliteControllerTest extends TelephonyTest {
        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
        setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
        setRadioPower(false);
        mSatelliteControllerUT.onCellularRadioPowerOffRequested();
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        processAllMessages();
        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
        processAllMessages();
@@ -1061,12 +1147,49 @@ public class SatelliteControllerTest extends TelephonyTest {

        resetSatelliteControllerUTToOnAndProvisionedState();
        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
        mSatelliteControllerUT.onCellularRadioPowerOffRequested();
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        processAllMessages();
        // Satellite should not be powered off since the feature flag oemEnabledSatelliteFlag is
        // disabled
        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);

        // Successfully disable satellite.
        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
        mIIntegerConsumerResults.clear();
        setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
                mIIntegerConsumer);
        processAllMessages();
        assertTrue(waitForIIntegerConsumerResult(1));
        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);

        // Fail to enable satellite when radio is being powered off.
        mIIntegerConsumerResults.clear();
        setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                mIIntegerConsumer);
        processAllMessages();
        assertTrue(waitForIIntegerConsumerResult(1));
        // Radio is being powered off, can not enable satellite
        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));

        // Modem failed to power off
        mSatelliteControllerUT.onPowerOffCellularRadioFailed();

        // Successfully enable satellite when radio is on.
        mIIntegerConsumerResults.clear();
        mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
        setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                mIIntegerConsumer);
        processAllMessages();
        assertTrue(waitForIIntegerConsumerResult(1));
        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
    }

    @Test
@@ -4712,5 +4835,21 @@ public class SatelliteControllerTest extends TelephonyTest {
                mIsSatelliteViaOemProvisioned = isProvisioned;
            }
        }

        public boolean isRadioOn() {
            synchronized (mIsRadioOnLock) {
                return mIsRadioOn;
            }
        }

        public boolean isRadioOffRequested() {
            synchronized (mIsRadioOnLock) {
                return mRadioOffRequested;
            }
        }

        public boolean isWaitForCellularModemOffTimerStarted() {
            return hasMessages(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT);
        }
    }
}