Loading src/java/com/android/internal/telephony/PhoneSwitcher.java +67 −51 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.MatchAllNetworkSpecifier; import android.net.Network; import android.net.NetworkCapabilities; Loading Loading @@ -255,20 +254,26 @@ public class PhoneSwitcher extends Handler { private ConnectivityManager mConnectivityManager; private final ConnectivityManager.NetworkCallback mDefaultNetworkCallback = new NetworkCallback() { private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback { public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; @Override public void onAvailable(Network network) { if (mConnectivityManager.getNetworkCapabilities(network) .hasTransport(TRANSPORT_CELLULAR)) { public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR) && SubscriptionManager.isValidSubscriptionId(mExpectedSubId) && mExpectedSubId == getSubIdFromNetworkSpecifier( networkCapabilities.getNetworkSpecifier())) { logDataSwitchEvent( mOpptDataSubId, mExpectedSubId, TelephonyEvent.EventState.EVENT_STATE_END, TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN); } mSwitchReason); removeDefaultNetworkChangeCallback(); } }; } } private final DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback(); /** * Method to get singleton instance. Loading Loading @@ -321,7 +326,7 @@ public class PhoneSwitcher extends Handler { // subscription. mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX; for (Phone phone : mPhones) { if (isCallActive(phone) || isCallActive(phone.getImsPhone())) { if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) { mPhoneIdInVoiceCall = phone.getPhoneId(); break; } Loading Loading @@ -448,10 +453,11 @@ public class PhoneSwitcher extends Handler { } case EVENT_PRIMARY_DATA_SUB_CHANGED: { if (onEvaluate(REQUESTS_UNCHANGED, "primary data subId changed")) { logDataSwitchEvent(mOpptDataSubId, logDataSwitchEvent(mPreferredDataSubId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); registerDefaultNetworkChangeCallback(); registerDefaultNetworkChangeCallback(mPreferredDataSubId, DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); } break; } Loading Loading @@ -521,10 +527,11 @@ public class PhoneSwitcher extends Handler { // fall through case EVENT_DATA_ENABLED_CHANGED: if (onEvaluate(REQUESTS_UNCHANGED, "EVENT_PRECISE_CALL_STATE_CHANGED")) { logDataSwitchEvent(mOpptDataSubId, logDataSwitchEvent(mPreferredDataSubId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); registerDefaultNetworkChangeCallback(); registerDefaultNetworkChangeCallback(mPreferredDataSubId, DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); } break; case EVENT_NETWORK_VALIDATION_DONE: { Loading Loading @@ -669,26 +676,21 @@ public class PhoneSwitcher extends Handler { } private void removeDefaultNetworkChangeCallback() { synchronized (mHasRegisteredDefaultNetworkChangeCallback) { if (mHasRegisteredDefaultNetworkChangeCallback) { mHasRegisteredDefaultNetworkChangeCallback = false; removeMessages(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK); mDefaultNetworkCallback.mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; mDefaultNetworkCallback.mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); } } } private void registerDefaultNetworkChangeCallback() { removeDefaultNetworkChangeCallback(); synchronized (mHasRegisteredDefaultNetworkChangeCallback) { mHasRegisteredDefaultNetworkChangeCallback = true; mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback); private void registerDefaultNetworkChangeCallback(int expectedSubId, int reason) { mDefaultNetworkCallback.mExpectedSubId = expectedSubId; mDefaultNetworkCallback.mSwitchReason = reason; mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this); sendMessageDelayed( obtainMessage(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK), DEFAULT_NETWORK_CHANGE_TIMEOUT_MS); } } private void collectRequestNetworkMetrics(NetworkRequest networkRequest) { // Request network for MMS will temporary disable the network on default data subscription, Loading Loading @@ -943,7 +945,8 @@ public class PhoneSwitcher extends Handler { } private int phoneIdForRequest(NetworkRequest netRequest) { int subId = getSubIdFromNetworkRequest(netRequest); int subId = getSubIdFromNetworkSpecifier(netRequest.networkCapabilities .getNetworkSpecifier()); if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId; if (subId == INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX; Loading Loading @@ -975,8 +978,7 @@ public class PhoneSwitcher extends Handler { return phoneId; } private int getSubIdFromNetworkRequest(NetworkRequest networkRequest) { NetworkSpecifier specifier = networkRequest.networkCapabilities.getNetworkSpecifier(); private int getSubIdFromNetworkSpecifier(NetworkSpecifier specifier) { if (specifier == null) { return DEFAULT_SUBSCRIPTION_ID; } Loading Loading @@ -1131,12 +1133,17 @@ public class PhoneSwitcher extends Handler { return; } // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId // if queued. removeMessages(EVENT_NETWORK_VALIDATION_DONE); int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) ? mPrimaryDataSubId : subId; if (mValidator.isValidating() && (!needValidation || subIdToValidate != mValidator.getSubIdInValidation())) { if (mValidator.isValidating()) { mValidator.stopValidation(); sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); mSetOpptSubCallback = null; } if (subId == mOpptDataSubId) { Loading @@ -1144,6 +1151,13 @@ public class PhoneSwitcher extends Handler { return; } logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); registerDefaultNetworkChangeCallback( subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId, DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); // If validation feature is not supported, set it directly. Otherwise, // start validation on the subscription first. if (mValidator.isValidationFeatureSupported() && needValidation) { Loading Loading @@ -1174,12 +1188,7 @@ public class PhoneSwitcher extends Handler { private void setOpportunisticSubscriptionInternal(int subId) { if (mOpptDataSubId != subId) { mOpptDataSubId = subId; if (onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed")) { logDataSwitchEvent(mOpptDataSubId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); registerDefaultNetworkChangeCallback(); } onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed"); } } Loading Loading @@ -1232,13 +1241,20 @@ public class PhoneSwitcher extends Handler { subId, needValidation ? 1 : 0, callback).sendToTarget(); } private boolean isCallActive(Phone phone) { private boolean isPhoneInVoiceCall(Phone phone) { if (phone == null) { return false; } // A phone in voice call might trigger data being switched to it. // We only report true if its precise call state is ACTIVE, ALERTING or HOLDING. // The reason is data switching is interrupting, so we only switch when necessary and // acknowledged by the users. For incoming call, we don't switch until answered // (RINGING -> ACTIVE), for outgoing call we don't switch until call is connected // in network (DIALING -> ALERTING). return (phone.getForegroundCall().getState() == Call.State.ACTIVE || phone.getForegroundCall().getState() == Call.State.ALERTING); || phone.getForegroundCall().getState() == Call.State.ALERTING || phone.getBackgroundCall().getState() == Call.State.HOLDING); } private void updateHalCommandToUse() { Loading @@ -1261,7 +1277,7 @@ public class PhoneSwitcher extends Handler { } private void logDataSwitchEvent(int subId, int state, int reason) { subId = subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId; log("logDataSwitchEvent subId " + subId + " state " + state + " reason " + reason); DataSwitch dataSwitch = new DataSwitch(); dataSwitch.state = state; dataSwitch.reason = reason; Loading tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java +129 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.internal.telephony; import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION; import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS; import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED; import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED; import static com.android.internal.telephony.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED; Loading Loading @@ -91,7 +95,13 @@ public class PhoneSwitcherTest extends TelephonyTest { @Mock private GsmCdmaCall mActiveCall; @Mock private GsmCdmaCall mHoldingCall; @Mock private GsmCdmaCall mInactiveCall; @Mock private ISetOpportunisticDataCallback mSetOpptDataCallback1; @Mock private ISetOpportunisticDataCallback mSetOpptDataCallback2; // The thread that mPhoneSwitcher will handle events in. private HandlerThread mHandlerThread; Loading @@ -116,6 +126,7 @@ public class PhoneSwitcherTest extends TelephonyTest { doReturn(Call.State.ACTIVE).when(mActiveCall).getState(); doReturn(Call.State.IDLE).when(mInactiveCall).getState(); doReturn(Call.State.HOLDING).when(mHoldingCall).getState(); } @After Loading Loading @@ -153,6 +164,7 @@ public class PhoneSwitcherTest extends TelephonyTest { clearInvocations(mActivePhoneSwitchHandler); setDefaultDataSubId(0); waitABit(); verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong()); Loading Loading @@ -504,14 +516,15 @@ public class PhoneSwitcherTest extends TelephonyTest { doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported(); initialize(numPhones, maxActivePhones); // Mark sub 2 as opportunistic. doReturn(true).when(mSubscriptionController).isOpportunistic(2); // Phone 0 has sub 1, phone 1 has sub 2. // Sub 1 is default data sub. // Both are active subscriptions are active sub, as they are in both active slots. setSlotIndexToSubId(0, 1); setSlotIndexToSubId(1, 2); setDefaultDataSubId(1); // Mark sub 2 as opportunistic. doReturn(true).when(mSubscriptionController).isOpportunistic(2); waitABit(); // Phone 0 (sub 1) should be activated as it has default data sub. assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId()); Loading Loading @@ -572,7 +585,6 @@ public class PhoneSwitcherTest extends TelephonyTest { notifyDataEnabled(false); notifyPhoneAsInCall(mPhone2); verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any()); waitABit(); assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0)); assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1)); Loading @@ -588,6 +600,13 @@ public class PhoneSwitcherTest extends TelephonyTest { verify(mMockRadioConfig).setPreferredDataModem(eq(0), any()); assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0)); assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1)); clearInvocations(mMockRadioConfig); // Phone2 has holding call, but data is turned off. So no data switching should happen. notifyPhoneAsInHoldingCall(mPhone2); verify(mMockRadioConfig).setPreferredDataModem(eq(1), any()); assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1)); assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0)); mHandlerThread.quit(); } Loading Loading @@ -861,6 +880,107 @@ public class PhoneSwitcherTest extends TelephonyTest { mHandlerThread.quit(); } @Test @SmallTest public void testSetPreferredDataCallback() throws Exception { final int numPhones = 2; final int maxActivePhones = 1; doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported(); initialize(numPhones, maxActivePhones); // Mark sub 2 as opportunistic. doReturn(true).when(mSubscriptionController).isOpportunistic(2); // Phone 0 has sub 1, phone 1 has sub 2. // Sub 1 is default data sub. // Both are active subscriptions are active sub, as they are in both active slots. setSlotIndexToSubId(0, 1); setSlotIndexToSubId(1, 2); setDefaultDataSubId(1); waitABit(); // Validating on sub 10 which is inactive. mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); // Switch to active subId without validating. Should always succeed. mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); // Validating on sub 1 and fails. clearInvocations(mSetOpptDataCallback1); mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); // Validating on sub 2 and succeeds. mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2); waitABit(); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); // Switching data back to primary and validation fails. clearInvocations(mSetOpptDataCallback2); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); // Switching data back to primary and succeeds. clearInvocations(mSetOpptDataCallback2); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(true, 1); waitABit(); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); // Back to back call on same subId. clearInvocations(mSetOpptDataCallback1); clearInvocations(mSetOpptDataCallback2); mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1); waitABit(); verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false), eq(mPhoneSwitcher.mValidationCallback)); doReturn(true).when(mCellularNetworkValidator).isValidating(); mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); verify(mSetOpptDataCallback2, never()).onComplete(anyInt()); // Validation succeeds. doReturn(false).when(mCellularNetworkValidator).isValidating(); mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2); waitABit(); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null); waitABit(); clearInvocations(mSetOpptDataCallback1); clearInvocations(mSetOpptDataCallback2); clearInvocations(mCellularNetworkValidator); // Back to back call, call 1 to switch to subId 2, call 2 to switch back. mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1); waitABit(); verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false), eq(mPhoneSwitcher.mValidationCallback)); doReturn(true).when(mCellularNetworkValidator).isValidating(); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2); waitABit(); // Call 1 should be cancelled and failed. Call 2 return success immediately as there's no // change. verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); mHandlerThread.quit(); } /* Private utility methods start here */ private void setAllPhonesInactive() { Loading @@ -878,6 +998,12 @@ public class PhoneSwitcherTest extends TelephonyTest { waitABit(); } private void notifyPhoneAsInHoldingCall(Phone phone) { doReturn(mHoldingCall).when(phone).getBackgroundCall(); mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED); waitABit(); } private void notifyPhoneAsInactive(Phone phone) { doReturn(mInactiveCall).when(phone).getForegroundCall(); mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED); Loading Loading
src/java/com/android/internal/telephony/PhoneSwitcher.java +67 −51 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.MatchAllNetworkSpecifier; import android.net.Network; import android.net.NetworkCapabilities; Loading Loading @@ -255,20 +254,26 @@ public class PhoneSwitcher extends Handler { private ConnectivityManager mConnectivityManager; private final ConnectivityManager.NetworkCallback mDefaultNetworkCallback = new NetworkCallback() { private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback { public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; @Override public void onAvailable(Network network) { if (mConnectivityManager.getNetworkCapabilities(network) .hasTransport(TRANSPORT_CELLULAR)) { public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR) && SubscriptionManager.isValidSubscriptionId(mExpectedSubId) && mExpectedSubId == getSubIdFromNetworkSpecifier( networkCapabilities.getNetworkSpecifier())) { logDataSwitchEvent( mOpptDataSubId, mExpectedSubId, TelephonyEvent.EventState.EVENT_STATE_END, TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN); } mSwitchReason); removeDefaultNetworkChangeCallback(); } }; } } private final DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback(); /** * Method to get singleton instance. Loading Loading @@ -321,7 +326,7 @@ public class PhoneSwitcher extends Handler { // subscription. mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX; for (Phone phone : mPhones) { if (isCallActive(phone) || isCallActive(phone.getImsPhone())) { if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) { mPhoneIdInVoiceCall = phone.getPhoneId(); break; } Loading Loading @@ -448,10 +453,11 @@ public class PhoneSwitcher extends Handler { } case EVENT_PRIMARY_DATA_SUB_CHANGED: { if (onEvaluate(REQUESTS_UNCHANGED, "primary data subId changed")) { logDataSwitchEvent(mOpptDataSubId, logDataSwitchEvent(mPreferredDataSubId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); registerDefaultNetworkChangeCallback(); registerDefaultNetworkChangeCallback(mPreferredDataSubId, DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL); } break; } Loading Loading @@ -521,10 +527,11 @@ public class PhoneSwitcher extends Handler { // fall through case EVENT_DATA_ENABLED_CHANGED: if (onEvaluate(REQUESTS_UNCHANGED, "EVENT_PRECISE_CALL_STATE_CHANGED")) { logDataSwitchEvent(mOpptDataSubId, logDataSwitchEvent(mPreferredDataSubId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); registerDefaultNetworkChangeCallback(); registerDefaultNetworkChangeCallback(mPreferredDataSubId, DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL); } break; case EVENT_NETWORK_VALIDATION_DONE: { Loading Loading @@ -669,26 +676,21 @@ public class PhoneSwitcher extends Handler { } private void removeDefaultNetworkChangeCallback() { synchronized (mHasRegisteredDefaultNetworkChangeCallback) { if (mHasRegisteredDefaultNetworkChangeCallback) { mHasRegisteredDefaultNetworkChangeCallback = false; removeMessages(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK); mDefaultNetworkCallback.mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; mDefaultNetworkCallback.mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); } } } private void registerDefaultNetworkChangeCallback() { removeDefaultNetworkChangeCallback(); synchronized (mHasRegisteredDefaultNetworkChangeCallback) { mHasRegisteredDefaultNetworkChangeCallback = true; mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback); private void registerDefaultNetworkChangeCallback(int expectedSubId, int reason) { mDefaultNetworkCallback.mExpectedSubId = expectedSubId; mDefaultNetworkCallback.mSwitchReason = reason; mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this); sendMessageDelayed( obtainMessage(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK), DEFAULT_NETWORK_CHANGE_TIMEOUT_MS); } } private void collectRequestNetworkMetrics(NetworkRequest networkRequest) { // Request network for MMS will temporary disable the network on default data subscription, Loading Loading @@ -943,7 +945,8 @@ public class PhoneSwitcher extends Handler { } private int phoneIdForRequest(NetworkRequest netRequest) { int subId = getSubIdFromNetworkRequest(netRequest); int subId = getSubIdFromNetworkSpecifier(netRequest.networkCapabilities .getNetworkSpecifier()); if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId; if (subId == INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX; Loading Loading @@ -975,8 +978,7 @@ public class PhoneSwitcher extends Handler { return phoneId; } private int getSubIdFromNetworkRequest(NetworkRequest networkRequest) { NetworkSpecifier specifier = networkRequest.networkCapabilities.getNetworkSpecifier(); private int getSubIdFromNetworkSpecifier(NetworkSpecifier specifier) { if (specifier == null) { return DEFAULT_SUBSCRIPTION_ID; } Loading Loading @@ -1131,12 +1133,17 @@ public class PhoneSwitcher extends Handler { return; } // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId // if queued. removeMessages(EVENT_NETWORK_VALIDATION_DONE); int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) ? mPrimaryDataSubId : subId; if (mValidator.isValidating() && (!needValidation || subIdToValidate != mValidator.getSubIdInValidation())) { if (mValidator.isValidating()) { mValidator.stopValidation(); sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); mSetOpptSubCallback = null; } if (subId == mOpptDataSubId) { Loading @@ -1144,6 +1151,13 @@ public class PhoneSwitcher extends Handler { return; } logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); registerDefaultNetworkChangeCallback( subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId, DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); // If validation feature is not supported, set it directly. Otherwise, // start validation on the subscription first. if (mValidator.isValidationFeatureSupported() && needValidation) { Loading Loading @@ -1174,12 +1188,7 @@ public class PhoneSwitcher extends Handler { private void setOpportunisticSubscriptionInternal(int subId) { if (mOpptDataSubId != subId) { mOpptDataSubId = subId; if (onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed")) { logDataSwitchEvent(mOpptDataSubId, TelephonyEvent.EventState.EVENT_STATE_START, DataSwitch.Reason.DATA_SWITCH_REASON_CBRS); registerDefaultNetworkChangeCallback(); } onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed"); } } Loading Loading @@ -1232,13 +1241,20 @@ public class PhoneSwitcher extends Handler { subId, needValidation ? 1 : 0, callback).sendToTarget(); } private boolean isCallActive(Phone phone) { private boolean isPhoneInVoiceCall(Phone phone) { if (phone == null) { return false; } // A phone in voice call might trigger data being switched to it. // We only report true if its precise call state is ACTIVE, ALERTING or HOLDING. // The reason is data switching is interrupting, so we only switch when necessary and // acknowledged by the users. For incoming call, we don't switch until answered // (RINGING -> ACTIVE), for outgoing call we don't switch until call is connected // in network (DIALING -> ALERTING). return (phone.getForegroundCall().getState() == Call.State.ACTIVE || phone.getForegroundCall().getState() == Call.State.ALERTING); || phone.getForegroundCall().getState() == Call.State.ALERTING || phone.getBackgroundCall().getState() == Call.State.HOLDING); } private void updateHalCommandToUse() { Loading @@ -1261,7 +1277,7 @@ public class PhoneSwitcher extends Handler { } private void logDataSwitchEvent(int subId, int state, int reason) { subId = subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId; log("logDataSwitchEvent subId " + subId + " state " + state + " reason " + reason); DataSwitch dataSwitch = new DataSwitch(); dataSwitch.state = state; dataSwitch.reason = reason; Loading
tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java +129 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.internal.telephony; import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION; import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS; import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED; import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED; import static com.android.internal.telephony.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED; Loading Loading @@ -91,7 +95,13 @@ public class PhoneSwitcherTest extends TelephonyTest { @Mock private GsmCdmaCall mActiveCall; @Mock private GsmCdmaCall mHoldingCall; @Mock private GsmCdmaCall mInactiveCall; @Mock private ISetOpportunisticDataCallback mSetOpptDataCallback1; @Mock private ISetOpportunisticDataCallback mSetOpptDataCallback2; // The thread that mPhoneSwitcher will handle events in. private HandlerThread mHandlerThread; Loading @@ -116,6 +126,7 @@ public class PhoneSwitcherTest extends TelephonyTest { doReturn(Call.State.ACTIVE).when(mActiveCall).getState(); doReturn(Call.State.IDLE).when(mInactiveCall).getState(); doReturn(Call.State.HOLDING).when(mHoldingCall).getState(); } @After Loading Loading @@ -153,6 +164,7 @@ public class PhoneSwitcherTest extends TelephonyTest { clearInvocations(mActivePhoneSwitchHandler); setDefaultDataSubId(0); waitABit(); verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong()); Loading Loading @@ -504,14 +516,15 @@ public class PhoneSwitcherTest extends TelephonyTest { doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported(); initialize(numPhones, maxActivePhones); // Mark sub 2 as opportunistic. doReturn(true).when(mSubscriptionController).isOpportunistic(2); // Phone 0 has sub 1, phone 1 has sub 2. // Sub 1 is default data sub. // Both are active subscriptions are active sub, as they are in both active slots. setSlotIndexToSubId(0, 1); setSlotIndexToSubId(1, 2); setDefaultDataSubId(1); // Mark sub 2 as opportunistic. doReturn(true).when(mSubscriptionController).isOpportunistic(2); waitABit(); // Phone 0 (sub 1) should be activated as it has default data sub. assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId()); Loading Loading @@ -572,7 +585,6 @@ public class PhoneSwitcherTest extends TelephonyTest { notifyDataEnabled(false); notifyPhoneAsInCall(mPhone2); verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any()); waitABit(); assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0)); assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1)); Loading @@ -588,6 +600,13 @@ public class PhoneSwitcherTest extends TelephonyTest { verify(mMockRadioConfig).setPreferredDataModem(eq(0), any()); assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0)); assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1)); clearInvocations(mMockRadioConfig); // Phone2 has holding call, but data is turned off. So no data switching should happen. notifyPhoneAsInHoldingCall(mPhone2); verify(mMockRadioConfig).setPreferredDataModem(eq(1), any()); assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1)); assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0)); mHandlerThread.quit(); } Loading Loading @@ -861,6 +880,107 @@ public class PhoneSwitcherTest extends TelephonyTest { mHandlerThread.quit(); } @Test @SmallTest public void testSetPreferredDataCallback() throws Exception { final int numPhones = 2; final int maxActivePhones = 1; doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported(); initialize(numPhones, maxActivePhones); // Mark sub 2 as opportunistic. doReturn(true).when(mSubscriptionController).isOpportunistic(2); // Phone 0 has sub 1, phone 1 has sub 2. // Sub 1 is default data sub. // Both are active subscriptions are active sub, as they are in both active slots. setSlotIndexToSubId(0, 1); setSlotIndexToSubId(1, 2); setDefaultDataSubId(1); waitABit(); // Validating on sub 10 which is inactive. mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); // Switch to active subId without validating. Should always succeed. mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); // Validating on sub 1 and fails. clearInvocations(mSetOpptDataCallback1); mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); // Validating on sub 2 and succeeds. mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2); waitABit(); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); // Switching data back to primary and validation fails. clearInvocations(mSetOpptDataCallback2); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); // Switching data back to primary and succeeds. clearInvocations(mSetOpptDataCallback2); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2); waitABit(); mPhoneSwitcher.mValidationCallback.onValidationResult(true, 1); waitABit(); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); // Back to back call on same subId. clearInvocations(mSetOpptDataCallback1); clearInvocations(mSetOpptDataCallback2); mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1); waitABit(); verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false), eq(mPhoneSwitcher.mValidationCallback)); doReturn(true).when(mCellularNetworkValidator).isValidating(); mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2); waitABit(); verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); verify(mSetOpptDataCallback2, never()).onComplete(anyInt()); // Validation succeeds. doReturn(false).when(mCellularNetworkValidator).isValidating(); mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2); waitABit(); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null); waitABit(); clearInvocations(mSetOpptDataCallback1); clearInvocations(mSetOpptDataCallback2); clearInvocations(mCellularNetworkValidator); // Back to back call, call 1 to switch to subId 2, call 2 to switch back. mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1); waitABit(); verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false), eq(mPhoneSwitcher.mValidationCallback)); doReturn(true).when(mCellularNetworkValidator).isValidating(); mPhoneSwitcher.trySetOpportunisticDataSubscription( SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2); waitABit(); // Call 1 should be cancelled and failed. Call 2 return success immediately as there's no // change. verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED); verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS); mHandlerThread.quit(); } /* Private utility methods start here */ private void setAllPhonesInactive() { Loading @@ -878,6 +998,12 @@ public class PhoneSwitcherTest extends TelephonyTest { waitABit(); } private void notifyPhoneAsInHoldingCall(Phone phone) { doReturn(mHoldingCall).when(phone).getBackgroundCall(); mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED); waitABit(); } private void notifyPhoneAsInactive(Phone phone) { doReturn(mInactiveCall).when(phone).getForegroundCall(); mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED); Loading