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

Commit b5fa01e5 authored by Sarah Kim's avatar Sarah Kim Committed by Automerger Merge Worker
Browse files

Merge "Add satellite modem state logic for calls" into udc-dev am: ea425103...

Merge "Add satellite modem state logic for calls" into udc-dev am: ea425103 am: fae2911d am: e04d6ed6

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/22904773



Change-Id: If3be231e817faf9713175b0e8324245174afed6d
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 69cd179c e04d6ed6
Loading
Loading
Loading
Loading
+28 −7
Original line number Diff line number Diff line
@@ -22,8 +22,10 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;

import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.satellite.SatelliteController;
import com.android.telephony.Rlog;

import java.util.ArrayList;
@@ -44,7 +46,7 @@ public class RadioOnHelper implements RadioOnStateListener.Callback {
    private RadioOnStateListener.Callback mCallback;
    private List<RadioOnStateListener> mListeners;
    private List<RadioOnStateListener> mInProgressListeners;
    private boolean mIsRadioOnCallingEnabled;
    private boolean mIsRadioReady;

    public RadioOnHelper(Context context) {
        mContext = context;
@@ -72,9 +74,9 @@ public class RadioOnHelper implements RadioOnStateListener.Callback {
     * class.
     *
     * This method kicks off the following sequence:
     * - Power on the radio for each Phone
     * - Listen for radio events telling us the radio has come up.
     * - Retry if we've gone a significant amount of time without any response from the radio.
     * - Power on the radio for each Phone and disable the satellite modem
     * - Listen for events telling us the radio has come up or the satellite modem is disabled.
     * - Retry if we've gone a significant amount of time without any response.
     * - Finally, clean up any leftover state.
     *
     * This method is safe to call from any thread, since it simply posts a message to the
@@ -87,7 +89,7 @@ public class RadioOnHelper implements RadioOnStateListener.Callback {
        setupListeners();
        mCallback = callback;
        mInProgressListeners.clear();
        mIsRadioOnCallingEnabled = false;
        mIsRadioReady = false;
        for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
            Phone phone = PhoneFactory.getPhone(i);
            if (phone == null) {
@@ -101,6 +103,9 @@ public class RadioOnHelper implements RadioOnStateListener.Callback {
                    && phone == phoneForEmergencyCall, timeoutCallbackInterval);
        }
        powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber);
        if (SatelliteController.getInstance().isSatelliteEnabled()) {
            powerOffSatellite(phoneForEmergencyCall);
        }
    }

    /**
@@ -141,16 +146,32 @@ public class RadioOnHelper implements RadioOnStateListener.Callback {
        }
    }

    /**
     * Attempt to power off the satellite modem. We'll eventually get an
     * onSatelliteModemStateChanged() callback when the satellite modem is successfully disabled.
     */
    private void powerOffSatellite(Phone phoneForEmergencyCall) {
        SatelliteController satelliteController = SatelliteController.getInstance();
        satelliteController.requestSatelliteEnabled(phoneForEmergencyCall.getSubId(),
                false /* enableSatellite */, false /* enableDemoMode */,
                new IIntegerConsumer.Stub() {
                    @Override
                    public void accept(int result) {

                    }
                });
    }

    /**
     * This method is called from multiple Listeners on the Main Looper. Synchronization is not
     * necessary.
     */
    @Override
    public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
        mIsRadioOnCallingEnabled |= isRadioReady;
        mIsRadioReady |= isRadioReady;
        mInProgressListeners.remove(listener);
        if (mCallback != null && mInProgressListeners.isEmpty()) {
            mCallback.onComplete(null, mIsRadioOnCallingEnabled);
            mCallback.onComplete(null, mIsRadioReady);
        }
    }

+73 −7
Original line number Diff line number Diff line
@@ -21,10 +21,14 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.satellite.ISatelliteStateCallback;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.satellite.SatelliteController;
import com.android.telephony.Rlog;

import java.util.Locale;
@@ -37,7 +41,8 @@ public class RadioOnStateListener {

    public interface Callback {
        /**
         * Receives the result of the RadioOnStateListener's attempt to turn on the radio.
         * Receives the result of the RadioOnStateListener's attempt to turn on the radio
         * and turn off the satellite modem.
         */
        void onComplete(RadioOnStateListener listener, boolean isRadioReady);

@@ -86,6 +91,8 @@ public class RadioOnStateListener {
    public static final int MSG_RADIO_OFF_OR_NOT_AVAILABLE = 5;
    public static final int MSG_IMS_CAPABILITY_CHANGED = 6;
    public static final int MSG_TIMEOUT_ONTIMEOUT_CALLBACK = 7;
    @VisibleForTesting
    public static final int MSG_SATELLITE_ENABLED_CHANGED = 8;

    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
@@ -123,6 +130,10 @@ public class RadioOnStateListener {
                    break;
                case MSG_TIMEOUT_ONTIMEOUT_CALLBACK:
                    onTimeoutCallbackTimeout();
                    break;
                case MSG_SATELLITE_ENABLED_CHANGED:
                    onSatelliteEnabledChanged();
                    break;
                default:
                    Rlog.w(TAG, String.format(Locale.getDefault(),
                        "handleMessage: unexpected message: %d.", msg.what));
@@ -131,8 +142,17 @@ public class RadioOnStateListener {
        }
    };

    private final ISatelliteStateCallback mSatelliteCallback = new ISatelliteStateCallback.Stub() {
        @Override
        public void onSatelliteModemStateChanged(int state) {
            mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED).sendToTarget();
        }
    };

    private Callback mCallback; // The callback to notify upon completion.
    private Phone mPhone; // The phone that will attempt to place the call.
    // SatelliteController instance to check whether satellite has been disabled.
    private SatelliteController mSatelliteController;
    private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
    // Whether this phone is selected to place emergency call. Can be true only if
    // mForEmergencyCall is true.
@@ -146,6 +166,7 @@ public class RadioOnStateListener {
     *
     * This method kicks off the following sequence:
     * - Listen for the service state change event telling us the radio has come up.
     * - Listen for the satellite state changed event telling us the satellite service is disabled.
     * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the
     *   radio.
     * - Finally, clean up any leftover state.
@@ -156,7 +177,7 @@ public class RadioOnStateListener {
     */
    public void waitForRadioOn(Phone phone, Callback callback,
            boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall,
            int onTimeoutCallbackInverval) {
            int onTimeoutCallbackInterval) {
        Rlog.d(TAG, "waitForRadioOn: Phone " + phone.getPhoneId());

        if (mPhone != null) {
@@ -169,7 +190,7 @@ public class RadioOnStateListener {
        args.arg2 = callback;
        args.arg3 = forEmergencyCall;
        args.arg4 = isSelectedPhoneForEmergencyCall;
        args.argi1 = onTimeoutCallbackInverval;
        args.argi1 = onTimeoutCallbackInterval;
        mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
    }

@@ -182,6 +203,7 @@ public class RadioOnStateListener {
            boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall,
            int onTimeoutCallbackInterval) {
        Rlog.d(TAG, "startSequenceInternal: Phone " + phone.getPhoneId());
        mSatelliteController = SatelliteController.getInstance();

        // First of all, clean up any state left over from a prior RadioOn call sequence. This
        // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while
@@ -198,9 +220,14 @@ public class RadioOnStateListener {
        // Register for RADIO_OFF to handle cases where emergency call is dialed before
        // we receive UNSOL_RESPONSE_RADIO_STATE_CHANGED with RADIO_OFF.
        registerForRadioOff();
        // Next step: when the SERVICE_STATE_CHANGED event comes in, we'll retry the call; see
        // onServiceStateChanged(). But also, just in case, start a timer to make sure we'll retry
        // the call even if the SERVICE_STATE_CHANGED event never comes in for some reason.
        if (mSatelliteController.isSatelliteEnabled()) {
            // Register for satellite modem state changed to notify when satellite is disabled.
            registerForSatelliteEnabledChanged();
        }
        // Next step: when the SERVICE_STATE_CHANGED or SATELLITE_ENABLED_CHANGED event comes in,
        // we'll retry the call; see onServiceStateChanged() and onSatelliteEnabledChanged().
        // But also, just in case, start a timer to make sure we'll retry the call even if the
        // SERVICE_STATE_CHANGED or SATELLITE_ENABLED_CHANGED events never come in for some reason.
        startRetryTimer();
        registerForImsCapabilityChanged();
        startOnTimeoutCallbackTimer();
@@ -292,6 +319,19 @@ public class RadioOnStateListener {
        }
    }

    private void onSatelliteEnabledChanged() {
        if (mPhone == null) {
            return;
        }
        if (isOkToCall(mPhone.getServiceState().getState(),
                mPhone.isVoiceOverCellularImsEnabled())) {
            onComplete(true);
            cleanup();
        } else {
            Rlog.d(TAG, "onSatelliteEnabledChanged: not ready to call yet, keep waiting.");
        }
    }

    /**
     * Callback to see if it is okay to call yet, given the current conditions.
     */
@@ -348,9 +388,20 @@ public class RadioOnStateListener {
                Rlog.w(TAG, "Hit MAX_NUM_RETRIES; giving up.");
                cleanup();
            } else {
                Rlog.d(TAG, "Trying (again) to turn on the radio.");
                Rlog.d(TAG, "Trying (again) to turn the radio on and satellite modem off.");
                mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
                        false);
                if (mSatelliteController.isSatelliteEnabled()) {
                    mSatelliteController.requestSatelliteEnabled(mPhone.getSubId(),
                            false /* enableSatellite */, false /* enableDemoMode */,
                            new IIntegerConsumer.Stub() {
                                @Override
                                public void accept(int result) {
                                    mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED)
                                            .sendToTarget();
                                }
                            });
                }
                startRetryTimer();
            }
        }
@@ -383,6 +434,7 @@ public class RadioOnStateListener {
        unregisterForServiceStateChanged();
        unregisterForRadioOff();
        unregisterForRadioOn();
        unregisterForSatelliteEnabledChanged();
        cancelRetryTimer();
        unregisterForImsCapabilityChanged();

@@ -442,6 +494,20 @@ public class RadioOnStateListener {
        mHandler.removeMessages(MSG_RADIO_ON); // Clean up any pending messages too
    }

    private void registerForSatelliteEnabledChanged() {
        mSatelliteController.registerForSatelliteModemStateChanged(
                mPhone.getSubId(), mSatelliteCallback);
    }

    private void unregisterForSatelliteEnabledChanged() {
        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        if (mPhone != null) {
            subId = mPhone.getSubId();
        }
        mSatelliteController.unregisterForSatelliteModemStateChanged(subId, mSatelliteCallback);
        mHandler.removeMessages(MSG_SATELLITE_ENABLED_CHANGED);
    }

    private void registerForImsCapabilityChanged() {
        unregisterForImsCapabilityChanged();
        mPhone.getServiceStateTracker()
+4 −0
Original line number Diff line number Diff line
@@ -115,6 +115,7 @@ import com.android.internal.telephony.metrics.PersistAtomsStorage;
import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.satellite.SatelliteController;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
@@ -267,6 +268,7 @@ public abstract class TelephonyTest {
    protected DataServiceManager mMockedWwanDataServiceManager;
    protected DataServiceManager mMockedWlanDataServiceManager;
    protected ServiceStateStats mServiceStateStats;
    protected SatelliteController mSatelliteController;

    // Initialized classes
    protected ActivityManager mActivityManager;
@@ -501,6 +503,7 @@ public abstract class TelephonyTest {
        mMockedWwanDataServiceManager = Mockito.mock(DataServiceManager.class);
        mMockedWlanDataServiceManager = Mockito.mock(DataServiceManager.class);
        mServiceStateStats = Mockito.mock(ServiceStateStats.class);
        mSatelliteController = Mockito.mock(SatelliteController.class);

        TelephonyManager.disableServiceHandleCaching();
        PropertyInvalidatedCache.disableForTestMode();
@@ -862,6 +865,7 @@ public abstract class TelephonyTest {
        replaceInstance(PhoneFactory.class, "sCommandsInterfaces", null,
                new CommandsInterface[] {mSimulatedCommands});
        replaceInstance(PhoneFactory.class, "sMetricsCollector", null, mMetricsCollector);
        replaceInstance(SatelliteController.class, "sInstance", null, mSatelliteController);

        setReady(false);
        // create default TestableLooper for test and add to list of monitored loopers
+73 −17
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        MockitoAnnotations.initMocks(this);
        mListener = new RadioOnStateListener();
        doReturn(mSST).when(mMockPhone).getServiceStateTracker();
        mMockPhone.mCi = mMockCi;
    }

    @After
@@ -84,22 +85,38 @@ public class RadioOnStateListenerTest extends TelephonyTest {
     */
    @Test
    public void testRegisterForCallback() {
        mMockPhone.mCi = mMockCi;
        mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);

        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);

        verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
        verify(mSatelliteController).unregisterForSatelliteModemStateChanged(anyInt(), any());
        verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
                eq(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
        verify(mSatelliteController, never()).registerForSatelliteModemStateChanged(
                anyInt(), any());

        verify(mMockCi).registerForOffOrNotAvailable(any(Handler.class),
                eq(RadioOnStateListener.MSG_RADIO_OFF_OR_NOT_AVAILABLE), isNull());
    }

    /**
     * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returns true,
     * so we are expecting
     * Ensure that we successfully register for the satellite modem state changed messages.
     */
    @Test
    public void testRegisterForSatelliteCallback() {
        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
        mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);

        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);

        verify(mSatelliteController).unregisterForSatelliteModemStateChanged(anyInt(), any());
        verify(mSatelliteController).registerForSatelliteModemStateChanged(anyInt(), any());
    }

    /**
     * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returns true after
     * service state changes, so we are expecting
     * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
     * return true.
     */
@@ -109,9 +126,7 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        state.setState(ServiceState.STATE_IN_SERVICE);
        when(mMockPhone.getServiceState()).thenReturn(state);
        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean()))
                .thenReturn(true);
        mMockPhone.mCi = mMockCi;
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(true);
        mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);

@@ -122,6 +137,29 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        verify(mCallback).onComplete(eq(mListener), eq(true));
    }

    /**
     * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int, boolean)} returns true after
     * satellite modem state changes, so we are expecting
     * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
     * return true.
     */
    @Test
    public void testSatelliteChangeState_OkToCallTrue() {
        ServiceState state = new ServiceState();
        state.setState(ServiceState.STATE_IN_SERVICE);
        when(mMockPhone.getServiceState()).thenReturn(state);
        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(true);
        mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);

        mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SATELLITE_ENABLED_CHANGED)
                .sendToTarget();

        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
        verify(mCallback).onComplete(eq(mListener), eq(true));
    }

    /**
     * We never receive a
     * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} because
@@ -132,10 +170,8 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        ServiceState state = new ServiceState();
        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean()))
                .thenReturn(false);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
        when(mMockPhone.getServiceState()).thenReturn(state);
        mMockPhone.mCi = mMockCi;
        mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);

@@ -158,18 +194,18 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        state.setState(ServiceState.STATE_POWER_OFF);
        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
        when(mMockPhone.getServiceState()).thenReturn(state);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean()))
                .thenReturn(false);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
        mListener.setTimeBetweenRetriesMillis(0/* ms */);
        mListener.setMaxNumRetries(2);

        // Wait for the timer to expire and check state manually in onRetryTimeout
        mMockPhone.mCi = mMockCi;
        mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
        waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);

        verify(mCallback).onComplete(eq(mListener), eq(false));
        verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(false), eq(false), eq(false));
        verify(mSatelliteController, never()).requestSatelliteEnabled(
                anyInt(), eq(false), eq(false), any());
    }

    @Test
@@ -178,18 +214,39 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        state.setState(ServiceState.STATE_POWER_OFF);
        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
        when(mMockPhone.getServiceState()).thenReturn(state);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean()))
                .thenReturn(false);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
        mListener.setTimeBetweenRetriesMillis(0/* ms */);
        mListener.setMaxNumRetries(2);

        // Wait for the timer to expire and check state manually in onRetryTimeout
        mListener.waitForRadioOn(mMockPhone, mCallback, true, true, 0);
        waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);

        verify(mCallback).onComplete(eq(mListener), eq(false));
        verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(true), eq(true), eq(false));
        verify(mSatelliteController, never()).requestSatelliteEnabled(
                anyInt(), eq(false), eq(false), any());
    }

    @Test
    public void testTimeout_RetryFailure_WithSatellite() {
        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
        ServiceState state = new ServiceState();
        state.setState(ServiceState.STATE_POWER_OFF);
        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
        when(mMockPhone.getServiceState()).thenReturn(state);
        when(mCallback.isOkToCall(eq(mMockPhone), anyInt(), anyBoolean())).thenReturn(false);
        mListener.setTimeBetweenRetriesMillis(0/* ms */);
        mListener.setMaxNumRetries(2);

        // Wait for the timer to expire and check state manually in onRetryTimeout
        mMockPhone.mCi = mMockCi;
        mListener.waitForRadioOn(mMockPhone, mCallback, true, true, 0);
        waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);

        verify(mCallback).onComplete(eq(mListener), eq(false));
        verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(true), eq(true), eq(false));
        verify(mSatelliteController, times(2)).requestSatelliteEnabled(
                anyInt(), eq(false), eq(false), any());
    }

    @Test
@@ -206,7 +263,6 @@ public class RadioOnStateListenerTest extends TelephonyTest {
        mListener.setMaxNumRetries(1);

        // Wait for the timer to expire and check state manually in onRetryTimeout
        mMockPhone.mCi = mMockCi;
        mListener.waitForRadioOn(mMockPhone, mCallback, true, true, 100);
        waitForDelayedHandlerAction(mListener.getHandler(), TIMEOUT_MS /* delay */, TIMEOUT_MS);