Loading src/java/com/android/internal/telephony/emergency/RadioOnHelper.java +28 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading @@ -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) { Loading @@ -101,6 +103,9 @@ public class RadioOnHelper implements RadioOnStateListener.Callback { && phone == phoneForEmergencyCall, timeoutCallbackInterval); } powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber); if (SatelliteController.getInstance().isSatelliteEnabled()) { powerOffSatellite(phoneForEmergencyCall); } } /** Loading Loading @@ -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); } } Loading src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java +73 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading Loading @@ -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 Loading Loading @@ -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)); Loading @@ -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. Loading @@ -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. Loading @@ -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) { Loading @@ -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(); } Loading @@ -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 Loading @@ -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(); Loading Loading @@ -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. */ Loading Loading @@ -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(); } } Loading Loading @@ -383,6 +434,7 @@ public class RadioOnStateListener { unregisterForServiceStateChanged(); unregisterForRadioOff(); unregisterForRadioOn(); unregisterForSatelliteEnabledChanged(); cancelRetryTimer(); unregisterForImsCapabilityChanged(); Loading Loading @@ -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() Loading tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +4 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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 Loading tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java +73 −17 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ public class RadioOnStateListenerTest extends TelephonyTest { MockitoAnnotations.initMocks(this); mListener = new RadioOnStateListener(); doReturn(mSST).when(mMockPhone).getServiceStateTracker(); mMockPhone.mCi = mMockCi; } @After Loading @@ -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. */ Loading @@ -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); Loading @@ -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 Loading @@ -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); Loading @@ -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 Loading @@ -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 Loading @@ -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); Loading Loading
src/java/com/android/internal/telephony/emergency/RadioOnHelper.java +28 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading @@ -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) { Loading @@ -101,6 +103,9 @@ public class RadioOnHelper implements RadioOnStateListener.Callback { && phone == phoneForEmergencyCall, timeoutCallbackInterval); } powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber); if (SatelliteController.getInstance().isSatelliteEnabled()) { powerOffSatellite(phoneForEmergencyCall); } } /** Loading Loading @@ -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); } } Loading
src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java +73 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading Loading @@ -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 Loading Loading @@ -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)); Loading @@ -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. Loading @@ -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. Loading @@ -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) { Loading @@ -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(); } Loading @@ -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 Loading @@ -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(); Loading Loading @@ -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. */ Loading Loading @@ -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(); } } Loading Loading @@ -383,6 +434,7 @@ public class RadioOnStateListener { unregisterForServiceStateChanged(); unregisterForRadioOff(); unregisterForRadioOn(); unregisterForSatelliteEnabledChanged(); cancelRetryTimer(); unregisterForImsCapabilityChanged(); Loading Loading @@ -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() Loading
tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +4 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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 Loading
tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java +73 −17 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ public class RadioOnStateListenerTest extends TelephonyTest { MockitoAnnotations.initMocks(this); mListener = new RadioOnStateListener(); doReturn(mSST).when(mMockPhone).getServiceStateTracker(); mMockPhone.mCi = mMockCi; } @After Loading @@ -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. */ Loading @@ -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); Loading @@ -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 Loading @@ -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); Loading @@ -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 Loading @@ -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 Loading @@ -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); Loading