blob: e13bacaa971295a81066297b53694b1c6ab3df62 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony;
import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG;
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.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_MULTI_SIM_CONFIG_CHANGED;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PhoneSwitcherTest extends TelephonyTest {
private static final int ACTIVE_PHONE_SWITCH = 1;
@Mock
private CommandsInterface mCommandsInterface0;
@Mock
private CommandsInterface mCommandsInterface1;
@Mock
private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
@Mock
private DataEnabledSettings mDataEnabledSettings2;
@Mock
private Handler mActivePhoneSwitchHandler;
@Mock
private GsmCdmaCall mActiveCall;
@Mock
private GsmCdmaCall mHoldingCall;
@Mock
private GsmCdmaCall mInactiveCall;
@Mock
private ISetOpportunisticDataCallback mSetOpptDataCallback1;
@Mock
private ISetOpportunisticDataCallback mSetOpptDataCallback2;
@Mock
CompletableFuture<Boolean> mFuturePhone;
private PhoneSwitcher mPhoneSwitcher;
private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
private ConnectivityManager mConnectivityManager;
// The messenger of PhoneSwitcher used to receive network requests.
private Messenger mNetworkProviderMessenger = null;
private int mDefaultDataSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private int[][] mSlotIndexToSubId;
private boolean[] mDataAllowed;
private int mActiveModemCount = 2;
private int mSupportedModemCount = 2;
private int mMaxDataAttachModemCount = 1;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
PhoneCapability phoneCapability = new PhoneCapability(1, 1, 0, null, false);
doReturn(phoneCapability).when(mPhoneConfigurationManager).getCurrentPhoneCapability();
doReturn(Call.State.ACTIVE).when(mActiveCall).getState();
doReturn(Call.State.IDLE).when(mInactiveCall).getState();
doReturn(Call.State.HOLDING).when(mHoldingCall).getState();
replaceInstance(Phone.class, "mCi", mPhone, mCommandsInterface0);
replaceInstance(Phone.class, "mCi", mPhone2, mCommandsInterface1);
}
@After
public void tearDown() throws Exception {
super.tearDown();
}
/**
* Test that a single phone case results in our phone being active and the RIL called
*/
@Test
@SmallTest
public void testRegister() throws Exception {
initialize();
// verify nothing has been done while there are no inputs
assertFalse("data allowed initially", mDataAllowed[0]);
assertFalse("data allowed initially", mDataAllowed[1]);
NetworkRequest internetNetworkRequest = addInternetNetworkRequest(null, 50);
assertFalse("phone active after request", mPhoneSwitcher
.shouldApplyNetworkRequest(internetNetworkRequest, 0));
// not registered yet - shouldn't inc
verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
setDefaultDataSubId(0);
verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
setSlotIndexToSubId(0, 0);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
// now try various things that should cause the active phone to switch:
// 1 lose default via default sub change
// 2 gain default via default sub change
// 3 lose default via sub->phone change
// 4 gain default via sub->phone change
// 5 lose default network request
// 6 gain subscription-specific request
// 7 lose via sub->phone change
// 8 gain via sub->phone change
// 9 lose subscription-specific request
// 10 don't switch phones when in emergency mode
// 1 lose default via default sub change
setDefaultDataSubId(1);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
setSlotIndexToSubId(1, 1);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
assertTrue("data not allowed", mDataAllowed[1]);
// 2 gain default via default sub change
setDefaultDataSubId(0);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[1]);
assertTrue("data not allowed", mDataAllowed[0]);
// 3 lose default via sub->phone change
setSlotIndexToSubId(0, 2);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 4 gain default via sub->phone change
setSlotIndexToSubId(0, 0);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 5 lose default network request
releaseNetworkRequest(internetNetworkRequest);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 6 gain subscription-specific request
NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 7 lose via sub->phone change
setSlotIndexToSubId(0, 1);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 8 gain via sub->phone change
setSlotIndexToSubId(0, 0);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 9 lose subscription-specific request
releaseNetworkRequest(specificInternetRequest);
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// 10 don't switch phones when in emergency mode
// not ready yet - Phone turns out to be hard to stub out
// phones[0].setInEmergencyCall(true);
// connectivityServiceMock.addDefaultRequest();
// processAllMessages();
// if (testHandler.getActivePhoneSwitchCount() != 11) {
// fail("after release of request, ActivePhoneSwitchCount not 11!");
// }
// if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
// if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
//
// phones[0].setInEmergencyCall(false);
// connectivityServiceMock.addDefaultRequest();
// processAllMessages();
// if (testHandler.getActivePhoneSwitchCount() != 12) {
// fail("after release of request, ActivePhoneSwitchCount not 11!");
// }
// if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
// if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
}
/**
* Test a multi-sim case with limited active phones:
* - lose default via default sub change
* - lose default via sub->phone change
* - gain default via sub->phone change
* - gain default via default sub change
* - lose default network request
* - gain subscription-specific request
* - lose via sub->phone change
* - gain via sub->phone change
* - lose subscription-specific request
* - tear down low priority phone when new request comes in
* - tear down low priority phone when sub change causes split
* - bring up low priority phone when sub change causes join
* - don't switch phones when in emergency mode
*/
@Test
@SmallTest
public void testPrioritization() throws Exception {
initialize();
addInternetNetworkRequest(null, 50);
setSlotIndexToSubId(0, 0);
setSlotIndexToSubId(1, 1);
setDefaultDataSubId(0);
mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
processAllMessages();
// verify initial conditions
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
// now start a higher priority connection on the other sub
addMmsNetworkRequest(1);
// After gain of network request, mActivePhoneSwitchHandler should be notified 2 times.
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
assertFalse("data allowed", mDataAllowed[0]);
assertTrue("data not allowed", mDataAllowed[1]);
}
/**
* Verify we don't send spurious DATA_ALLOWED calls when another NetworkProvider
* wins (ie, switch to wifi).
*/
@Test
@SmallTest
public void testHigherPriorityDefault() throws Exception {
initialize();
addInternetNetworkRequest(null, 50);
setSlotIndexToSubId(0, 0);
setSlotIndexToSubId(1, 1);
setDefaultDataSubId(0);
// Phone 0 should be active
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
addInternetNetworkRequest(null, 100);
// should be no change
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
addInternetNetworkRequest(null, 0);
// should be no change
assertTrue("data not allowed", mDataAllowed[0]);
assertFalse("data allowed", mDataAllowed[1]);
}
/**
* Verify testSetPreferredData.
* When preferredData is set, it overwrites defaultData sub to be active sub in single
* active phone mode. If it's unset (to DEFAULT_SUBSCRIPTION_ID), defaultData sub becomes
* active one.
*/
@Test
@SmallTest
public void testSetPreferredData() throws Exception {
initialize();
// 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);
doReturn(true).when(mSubscriptionController).isOpportunistic(2);
// Notify phoneSwitcher about default data sub and default network request.
addInternetNetworkRequest(null, 50);
// Phone 0 (sub 1) should be activated as it has default data sub.
assertTrue(mDataAllowed[0]);
// Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
assertFalse(mDataAllowed[0]);
assertTrue(mDataAllowed[1]);
// Unset preferred sub should make default data sub (phone 0 / sub 1) activated again.
mPhoneSwitcher.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
assertTrue(mDataAllowed[0]);
assertFalse(mDataAllowed[1]);
}
@Test
@SmallTest
public void testSetPreferredDataModemCommand() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
ACTIVE_PHONE_SWITCH, null);
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
clearInvocations(mMockRadioConfig);
clearInvocations(mActivePhoneSwitchHandler);
// 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);
// Phone 0 (sub 1) should be preferred data phone as it has default data sub.
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
clearInvocations(mMockRadioConfig);
clearInvocations(mActivePhoneSwitchHandler);
// Notify phoneSwitcher about default data sub and default network request.
// It shouldn't change anything.
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
NetworkRequest mmsRequest = addMmsNetworkRequest(2);
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 1));
// Set sub 2 as preferred sub should make phone 1 preferredDataModem
doReturn(true).when(mSubscriptionController).isOpportunistic(2);
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 0));
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 1));
clearInvocations(mMockRadioConfig);
clearInvocations(mActivePhoneSwitchHandler);
// Unset preferred sub should make phone0 preferredDataModem again.
mPhoneSwitcher.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 1));
// SetDataAllowed should never be triggered.
verify(mCommandsInterface0, never()).setDataAllowed(anyBoolean(), any());
verify(mCommandsInterface1, never()).setDataAllowed(anyBoolean(), any());
}
@Test
@SmallTest
public void testSetPreferredDataWithValidation() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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);
// Phone 0 (sub 1) should be activated as it has default data sub.
assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
// Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
// Validation failed. Preferred data sub should remain 1, data phone should remain 0.
mPhoneSwitcher.mValidationCallback.onValidationDone(false, 2);
processAllMessages();
assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
// Validation succeeds. Preferred data sub changes to 2, data phone changes to 1.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
processAllMessages();
assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
// Switching data back to primary (subId 1) with customized validation timeout.
long timeout = 1234;
mContextFixture.getCarrierConfigBundle().putLong(
KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, timeout);
mPhoneSwitcher.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, null);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(1), eq(timeout), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
mPhoneSwitcher.mValidationCallback.onValidationDone(true, 1);
processAllMessages();
assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
}
@Test
@SmallTest
public void testNonDefaultDataPhoneInCall() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
clearInvocations(mMockRadioConfig);
setAllPhonesInactive();
// Initialization done.
// Phone2 has active call, but data is turned off. So no data switching should happen.
notifyDataEnabled(false);
notifyPhoneAsInCall(mPhone2);
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
// Phone2 has active call. So data switch to it.
notifyDataEnabled(true);
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
clearInvocations(mMockRadioConfig);
// Phone2 call ended. So data switch back to default data sub.
notifyPhoneAsInactive(mPhone2);
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));
}
@Test
@SmallTest
public void testNetworkRequestOnNonDefaultData() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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);
NetworkRequest internetRequest = addInternetNetworkRequest(2, 50);
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
// Restricted network request will should be applied.
internetRequest = addInternetNetworkRequest(2, 50, true);
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
}
@Test
@SmallTest
public void testEmergencyOverrideSuccessBeforeCallStarts() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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.
setMsimDefaultDataSubId(1);
clearInvocations(mMockRadioConfig);
// override the phone ID in prep for emergency call
mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
// Make sure the correct broadcast is sent out for the overridden phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(2));
}
@Test
@SmallTest
public void testEmergencyOverrideNoDdsChange() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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.
setMsimDefaultDataSubId(1);
clearInvocations(mMockRadioConfig);
// override the phone ID in prep for emergency call
mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, mFuturePhone);
processAllMessages();
// The radio command should never be called because the DDS hasn't changed.
verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
processAllMessages();
verify(mFuturePhone).complete(true);
}
@Test
@SmallTest
public void testEmergencyOverrideEndSuccess() throws Exception {
PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS = 500;
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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.
setMsimDefaultDataSubId(1);
setAllPhonesInactive();
clearInvocations(mMockRadioConfig);
clearInvocations(mTelephonyRegistryManager);
// override the phone ID in prep for emergency call
mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
// Start and end the emergency call, which will start override timer
notifyPhoneAsInCall(mPhone2);
notifyPhoneAsInactive(mPhone2);
clearInvocations(mTelephonyRegistryManager);
// Verify that the DDS is successfully switched back after 1 second + base ECBM timeout
moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 1000);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@Test
@SmallTest
public void testEmergencyOverrideEcbmStartEnd() throws Exception {
PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS = 500;
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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.
setMsimDefaultDataSubId(1);
setAllPhonesInactive();
clearInvocations(mMockRadioConfig);
clearInvocations(mTelephonyRegistryManager);
// override the phone ID in prep for emergency call
mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
// Start and end the emergency call, which will start override timer
notifyPhoneAsInCall(mPhone2);
notifyPhoneAsInactive(mPhone2);
// Start ECBM
Message ecbmMessage = getEcbmRegistration(mPhone2);
notifyEcbmStart(mPhone2, ecbmMessage);
// DDS should not be switched back until ECBM ends, make sure there is no further
// interaction.
moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 2000);
processAllMessages();
verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(2));
// End ECBM
clearInvocations(mTelephonyRegistryManager);
ecbmMessage = getEcbmRegistration(mPhone2);
notifyEcbmEnd(mPhone2, ecbmMessage);
// Verify that the DDS is successfully switched back after 1 second.
moveTimeForward(1000);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@Test
@SmallTest
public void testEmergencyOverrideNoCallStart() throws Exception {
PhoneSwitcher.DEFAULT_DATA_OVERRIDE_TIMEOUT_MS = 500;
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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.
setMsimDefaultDataSubId(1);
setAllPhonesInactive();
clearInvocations(mMockRadioConfig);
clearInvocations(mTelephonyRegistryManager);
// override the phone ID in prep for emergency call
mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
// Do not start the call and make sure the override is removed once the timeout expires
moveTimeForward(PhoneSwitcher.DEFAULT_DATA_OVERRIDE_TIMEOUT_MS);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@Test
@SmallTest
public void testEmergencyOverrideMultipleOverrideRequests() throws Exception {
PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS = 500;
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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.
setMsimDefaultDataSubId(1);
setAllPhonesInactive();
clearInvocations(mMockRadioConfig);
clearInvocations(mTelephonyRegistryManager);
// override the phone ID in prep for emergency call
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>();
CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
futurePhone.whenComplete((r, error) -> queue.offer(r));
mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
sendPreferredDataSuccessResult(1);
processAllMessages();
Boolean result = queue.poll();
assertNotNull(result);
assertTrue(result);
// try override the phone ID again while there is an existing override for a different phone
futurePhone = new CompletableFuture<>();
futurePhone.whenComplete((r, error) -> queue.offer(r));
mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, futurePhone);
processAllMessages();
result = queue.poll();
assertNotNull(result);
assertFalse(result);
verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
// Start and end the emergency call, which will start override timer
notifyPhoneAsInCall(mPhone2);
notifyPhoneAsInactive(mPhone2);
// Verify that the DDS is successfully switched back after 1 second + base ECBM timeout
moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 1000);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@Test
@SmallTest
public void testSetPreferredDataCallback() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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);
// Validating on sub 10 which is inactive.
mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
// Switch to active subId without validating. Should always succeed.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
// Validating on sub 1 and fails.
clearInvocations(mSetOpptDataCallback1);
mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
// Validating on sub 2 and succeeds.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
processAllMessages();
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);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
processAllMessages();
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);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onValidationDone(true, 1);
processAllMessages();
verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
// Back to back call on same subId.
clearInvocations(mSetOpptDataCallback1);
clearInvocations(mSetOpptDataCallback2);
clearInvocations(mCellularNetworkValidator);
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
processAllMessages();
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
verify(mSetOpptDataCallback2, never()).onComplete(anyInt());
// Validation succeeds.
doReturn(false).when(mCellularNetworkValidator).isValidating();
mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
processAllMessages();
verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
mPhoneSwitcher.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
processAllMessages();
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
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);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
mPhoneSwitcher.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
processAllMessages();
// 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);
}
@Test
@SmallTest
public void testMultiSimConfigChange() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
mActiveModemCount = 1;
initialize();
sendPreferredDataSuccessResult(0);
// 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);
setDefaultDataSubId(1);
setNumPhones(2, 2);
AsyncResult result = new AsyncResult(null, 2, null);
Message.obtain(mPhoneSwitcher, EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
processAllMessages();
verify(mPhone2).registerForEmergencyCallToggle(any(), anyInt(), any());
verify(mPhone2).registerForPreciseCallStateChanged(any(), anyInt(), any());
verify(mDataEnabledSettings2).registerForDataEnabledChanged(any(), anyInt(), any());
clearInvocations(mMockRadioConfig);
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(2);
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
}
@Test
@SmallTest
public void testValidationOffSwitch_shouldSwitchOnNetworkAvailable() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
clearInvocations(mMockRadioConfig);
setAllPhonesInactive();
// Initialization done.
doReturn(true).when(mSubscriptionController).isOpportunistic(2);
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
// Network available on different sub. Should do nothing.
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
processAllMessages();
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
// Network available on corresponding sub. Should confirm switch.
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
}
@Test
@SmallTest
public void testValidationOffSwitch_shouldSwitchOnTimeOut() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
// 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);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
clearInvocations(mMockRadioConfig);
setAllPhonesInactive();
// Initialization done.
doReturn(true).when(mSubscriptionController).isOpportunistic(2);
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
processAllMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
// Validation failed on different sub. Should do nothing.
mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
processAllMessages();
verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
// Network available on corresponding sub. Should confirm switch.
mPhoneSwitcher.mValidationCallback.onValidationDone(false, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
}
/* Private utility methods start here */
private void setAllPhonesInactive() {
doReturn(mInactiveCall).when(mPhone).getForegroundCall();
doReturn(mInactiveCall).when(mPhone).getBackgroundCall();
doReturn(mInactiveCall).when(mPhone).getRingingCall();
doReturn(mInactiveCall).when(mPhone2).getForegroundCall();
doReturn(mInactiveCall).when(mPhone2).getBackgroundCall();
doReturn(mInactiveCall).when(mPhone2).getRingingCall();
}
private void notifyPhoneAsInCall(Phone phone) {
doReturn(mActiveCall).when(phone).getForegroundCall();
mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyPhoneAsInHoldingCall(Phone phone) {
doReturn(mHoldingCall).when(phone).getBackgroundCall();
mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyPhoneAsInactive(Phone phone) {
doReturn(mInactiveCall).when(phone).getForegroundCall();
mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
processAllMessages();
}
private void notifyDataEnabled(boolean dataEnabled) {
doReturn(dataEnabled).when(mDataEnabledSettings).isDataEnabled(anyInt());
doReturn(dataEnabled).when(mDataEnabledSettings2).isDataEnabled(anyInt());
mPhoneSwitcher.sendEmptyMessage(EVENT_DATA_ENABLED_CHANGED);
processAllMessages();
}
private Message getEcbmRegistration(Phone phone) {
ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
verify(phone).registerForEmergencyCallToggle(handlerCaptor.capture(), intCaptor.capture(),
any());
assertNotNull(handlerCaptor.getValue());
assertNotNull(intCaptor.getValue());
Message message = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue());
return message;
}
private void notifyEcbmStart(Phone phone, Message ecmMessage) {
doReturn(mInactiveCall).when(phone).getForegroundCall();
doReturn(true).when(phone).isInEcm();
ecmMessage.sendToTarget();
processAllMessages();
}
private void notifyEcbmEnd(Phone phone, Message ecmMessage) {
doReturn(false).when(phone).isInEcm();
ecmMessage.sendToTarget();
processAllMessages();
}
private void sendPreferredDataSuccessResult(int phoneId) {
// make sure the radio command is called and then send a success result
processAllMessages();
ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
verify(mMockRadioConfig).setPreferredDataModem(eq(phoneId), msgCaptor.capture());
assertNotNull(msgCaptor.getValue());
// Send back successful result
AsyncResult.forMessage(msgCaptor.getValue(), null, null);
msgCaptor.getValue().sendToTarget();
processAllMessages();
}
private void setMsimDefaultDataSubId(int defaultDataSub) throws Exception {
for (int i = 0; i < mActiveModemCount; i++) {
setSlotIndexToSubId(i, i + 1);
}
setDefaultDataSubId(defaultDataSub);
NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
for (int i = 0; i < mActiveModemCount; i++) {
if (defaultDataSub == (i + 1)) {
// sub id is always phoneId+1 for testing
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, i));
} else {
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, i));
}
}
}
private void sendDefaultDataSubChanged() {
final Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.sendBroadcast(intent);
processAllMessages();
}
private void initialize() throws Exception {
setNumPhones(mActiveModemCount, mSupportedModemCount);
initializeSubControllerMock();
initializeCommandInterfacesMock();
initializeTelRegistryMock();
initializeConnManagerMock();
mPhoneSwitcher = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
processAllMessages();
verify(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(any(), any());
}
/**
* Certain variables needs initialized depending on number of phones.
*/
private void setNumPhones(int activeModemCount, int supportedModemCount) throws Exception {
mDataAllowed = new boolean[supportedModemCount];
mSlotIndexToSubId = new int[supportedModemCount][];
doReturn(0).when(mPhone).getPhoneId();
doReturn(1).when(mPhone2).getPhoneId();
doReturn(true).when(mPhone2).isUserDataEnabled();
doReturn(mDataEnabledSettings2).when(mPhone2).getDataEnabledSettings();
for (int i = 0; i < supportedModemCount; i++) {
mSlotIndexToSubId[i] = new int[1];
mSlotIndexToSubId[i][0] = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
doReturn(activeModemCount).when(mTelephonyManager).getPhoneCount();
doReturn(activeModemCount).when(mTelephonyManager).getActiveModemCount();
doReturn(supportedModemCount).when(mTelephonyManager).getSupportedModemCount();
if (activeModemCount == 1) {
mPhones = new Phone[]{mPhone};
} else if (activeModemCount == 2) {
mPhones = new Phone[]{mPhone, mPhone2};
}
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
}
private void initializeCommandInterfacesMock() {
// Tell PhoneSwitcher that radio is on.
doAnswer(invocation -> {
Handler handler = (Handler) invocation.getArguments()[0];
int message = (int) invocation.getArguments()[1];
Object obj = invocation.getArguments()[2];
handler.obtainMessage(message, obj).sendToTarget();
return null;
}).when(mCommandsInterface0).registerForAvailable(any(), anyInt(), any());
// Store values of dataAllowed in mDataAllowed[] for easier checking.
doAnswer(invocation -> {
mDataAllowed[0] = (boolean) invocation.getArguments()[0];
return null;
}).when(mCommandsInterface0).setDataAllowed(anyBoolean(), any());
if (mSupportedModemCount > 1) {
doAnswer(invocation -> {
mDataAllowed[1] = (boolean) invocation.getArguments()[0];
return null;
}).when(mCommandsInterface1).setDataAllowed(anyBoolean(), any());
}
}
/**
* Store subChangedListener of PhoneSwitcher so that testing can notify
* PhoneSwitcher of sub change.
*/
private void initializeTelRegistryMock() throws Exception {
doAnswer(invocation -> {
SubscriptionManager.OnSubscriptionsChangedListener subChangedListener =
(SubscriptionManager.OnSubscriptionsChangedListener) invocation.getArguments()[0];
mSubChangedListener = subChangedListener;
mSubChangedListener.onSubscriptionsChanged();
return null;
}).when(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(any(), any());
}
/**
* Capture mNetworkProviderMessenger so that testing can request or release
* network requests on PhoneSwitcher.
*/
private void initializeConnManagerMock() {
mConnectivityManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
doAnswer(invocation -> {
mNetworkProviderMessenger =
((NetworkProvider) invocation.getArgument(0)).getMessenger();
return null;
}).when(mConnectivityManager).registerNetworkProvider(any());
}
/**
* Capture mNetworkProviderMessenger so that testing can request or release
* network requests on PhoneSwitcher.
*/
private void initializeSubControllerMock() {
doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
doAnswer(invocation -> {
int phoneId = (int) invocation.getArguments()[0];
if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
} else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
return mSlotIndexToSubId[0][0];
} else {
return mSlotIndexToSubId[phoneId][0];
}
}).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
doAnswer(invocation -> {
int subId = (int) invocation.getArguments()[0];
if (!SubscriptionManager.isUsableSubIdValue(subId)) return false;
for (int i = 0; i < mSlotIndexToSubId.length; i++) {
if (mSlotIndexToSubId[i][0] == subId) return true;
}
return false;
}).when(mSubscriptionController).isActiveSubId(anyInt());
}
private void setDefaultDataSubId(int defaultDataSub) {
mDefaultDataSub = defaultDataSub;
doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
sendDefaultDataSubChanged();
}
private void setSlotIndexToSubId(int slotId, int subId) {
mSlotIndexToSubId[slotId][0] = subId;
}
/**
* Create an internet PDN network request and send it to PhoneSwitcher.
*/
private NetworkRequest addInternetNetworkRequest(Integer subId, int score) throws Exception {
return addInternetNetworkRequest(subId, score, false);
}
private NetworkRequest addInternetNetworkRequest(Integer subId, int score, boolean restricted)
throws Exception {
NetworkCapabilities netCap = (new NetworkCapabilities())
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (restricted) {
netCap.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
if (subId != null) {
netCap.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(subId).build());
}
NetworkRequest networkRequest = new NetworkRequest(netCap, ConnectivityManager.TYPE_NONE,
0, NetworkRequest.Type.REQUEST);
Message message = Message.obtain();
message.what = android.net.NetworkProvider.CMD_REQUEST_NETWORK;
message.arg1 = score;
message.obj = networkRequest;
mNetworkProviderMessenger.send(message);
processAllMessages();
return networkRequest;
}
/**
* Create a mms PDN network request and send it to PhoneSwitcher.
*/
private NetworkRequest addMmsNetworkRequest(Integer subId) throws Exception {
NetworkCapabilities netCap = (new NetworkCapabilities())
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (subId != null) {
netCap.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(subId).build());
}
NetworkRequest networkRequest = new NetworkRequest(netCap, ConnectivityManager.TYPE_NONE,
1, NetworkRequest.Type.REQUEST);
Message message = Message.obtain();
message.what = android.net.NetworkProvider.CMD_REQUEST_NETWORK;
message.arg1 = 50; // Score
message.obj = networkRequest;
mNetworkProviderMessenger.send(message);
processAllMessages();
return networkRequest;
}
/**
* Tell PhoneSwitcher to release a network request.
*/
private void releaseNetworkRequest(NetworkRequest networkRequest) throws Exception {
Message message = Message.obtain();
message.what = android.net.NetworkProvider.CMD_CANCEL_REQUEST;
message.obj = networkRequest;
mNetworkProviderMessenger.send(message);
processAllMessages();
}
}