aboutsummaryrefslogtreecommitdiff
path: root/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java')
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java380
1 files changed, 380 insertions, 0 deletions
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
new file mode 100644
index 0000000000..7ac3a170ad
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2023 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.data;
+
+import static android.telephony.SubscriptionManager.DEFAULT_PHONE_INDEX;
+
+import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_DATA_SETTINGS_CHANGED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AutoDataSwitchControllerTest extends TelephonyTest {
+ private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+ private static final int EVENT_DISPLAY_INFO_CHANGED = 2;
+ private static final int EVENT_EVALUATE_AUTO_SWITCH = 3;
+ private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
+ private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
+
+ private static final int PHONE_1 = 0;
+ private static final int SUB_1 = 1;
+ private static final int PHONE_2 = 1;
+ private static final int SUB_2 = 2;
+ private static final int MAX_RETRY = 5;
+ // Mocked
+ private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
+
+ private int mDefaultDataSub;
+ private AutoDataSwitchController mAutoDataSwitchControllerUT;
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mMockedPhoneSwitcherCallback =
+ mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
+
+ doReturn(PHONE_1).when(mPhone).getPhoneId();
+ doReturn(SUB_1).when(mPhone).getSubId();
+
+ doReturn(PHONE_2).when(mPhone2).getPhoneId();
+ doReturn(SUB_2).when(mPhone2).getSubId();
+
+ doReturn(SUB_1).when(mSubscriptionManagerService).getSubId(PHONE_1);
+ doReturn(SUB_2).when(mSubscriptionManagerService).getSubId(PHONE_2);
+
+ mPhones = new Phone[]{mPhone, mPhone2};
+ for (Phone phone : mPhones) {
+ doReturn(mSST).when(phone).getServiceStateTracker();
+ doReturn(mDisplayInfoController).when(phone).getDisplayInfoController();
+ doReturn(mSignalStrengthController).when(phone).getSignalStrengthController();
+ doReturn(mSignalStrength).when(phone).getSignalStrength();
+ doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
+ .when(phone).isUserDataEnabled();
+ }
+ doReturn(new int[mPhones.length]).when(mSubscriptionManagerService)
+ .getActiveSubIdList(true);
+ doAnswer(invocation -> {
+ int subId = (int) invocation.getArguments()[0];
+
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) return null;
+
+ int slotIndex = subId == SUB_1 ? PHONE_1 : PHONE_2;
+ return new SubscriptionInfoInternal.Builder()
+ .setSimSlotIndex(slotIndex).setId(subId).build();
+ }).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+
+ // Change resource overlay
+ doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
+ doReturn(1L).when(mDataConfigManager)
+ .getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry();
+
+ setDefaultDataSubId(SUB_1);
+ doReturn(PHONE_1).when(mPhoneSwitcher).getPreferredDataPhoneId();
+
+ mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+ mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mAutoDataSwitchControllerUT = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testCancelSwitch_onPrimary() {
+ // 0. When all conditions met
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+
+ // Verify attempting to switch
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
+
+ // 1. Service state becomes not ideal - primary is available again
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+ // 2.1 User data disabled on primary SIM
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+ // 2.2 Auto switch feature is disabled
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ doReturn(false).when(mPhone2).isDataAllowed();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+ // 3.1 No default network
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI));
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ }
+
+ @Test
+ public void testOnNonDdsSwitchBackToPrimary() {
+ doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+
+ prepareIdealUsesNonDdsCondition();
+ // 1.1 service state changes - primary becomes available again, require validation
+ serviceStateChanged(PHONE_1,
+ NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*need validate*/);
+ processAllFutureMessages();
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ true/*needValidation*/);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 1.2 service state changes - secondary becomes unavailable, NO need validation
+ serviceStateChanged(PHONE_1,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*no need*/);
+ processAllFutureMessages();
+ // The later validation requirement overrides the previous
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 2.1 User data disabled on primary SIM, no need validation
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+ EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 2.2 Auto switch feature is disabled, no need validation
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(false).when(mPhone2).isDataAllowed();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+ EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 3.1 Default network is active on non-cellular transport
+ mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI));
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+ }
+
+ @Test
+ public void testCancelSwitch_onSecondary() {
+ doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+ prepareIdealUsesNonDdsCondition();
+
+ // attempts the switch back due to secondary becomes ROAMING
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+
+ // cancel the switch back attempt due to secondary back to HOME
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ }
+
+ @Test
+ public void testValidationFailedRetry() {
+ prepareIdealUsesNonDdsCondition();
+
+ for (int i = 0; i < MAX_RETRY; i++) {
+ mAutoDataSwitchControllerUT.evaluateRetryOnValidationFailed();
+ processAllFutureMessages();
+ }
+ verify(mMockedPhoneSwitcherCallback, times(MAX_RETRY))
+ .onRequireValidation(PHONE_2, true /*need validation*/);
+ }
+
+ @Test
+ public void testExemptPingTest() {
+ // Change resource overlay
+ doReturn(false).when(mDataConfigManager)
+ .isPingTestBeforeAutoDataSwitchRequired();
+ mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+ mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+
+ //1. DDS -> nDDS, verify callback doesn't require validation
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, false/*needValidation*/);
+
+ //2. nDDS -> DDS, verify callback doesn't require validation
+ doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+ }
+
+ @Test
+ public void testSetNotification() {
+ NotificationManager notificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ SubscriptionInfo mockedInfo = mock(SubscriptionInfo.class);
+ doReturn(false).when(mockedInfo).isOpportunistic();
+ doReturn(mockedInfo).when(mSubscriptionManagerService).getSubscriptionInfo(anyInt());
+
+ // First switch is not due to auto, so no notification.
+ mAutoDataSwitchControllerUT.displayAutoDataSwitchNotification(PHONE_2, false);
+ verify(mSubscriptionManagerService, never()).getSubscriptionInfo(SUB_2);
+
+ // Switch is due to auto, show notification.
+ mAutoDataSwitchControllerUT.displayAutoDataSwitchNotification(PHONE_2, true);
+ verify(notificationManager).notify(any(), anyInt(), any());
+ verify(mSubscriptionManagerService).getSubscriptionInfo(SUB_2);
+
+ // Switch is due to auto, but already shown notification, hide the notification.
+ mAutoDataSwitchControllerUT.displayAutoDataSwitchNotification(PHONE_2, true);
+ verify(notificationManager).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testMultiSimConfigChanged() {
+ // Test Dual -> Single
+ mAutoDataSwitchControllerUT.onMultiSimConfigChanged(1);
+
+ verify(mDisplayInfoController).unregisterForTelephonyDisplayInfoChanged(any());
+ verify(mSignalStrengthController).unregisterForSignalStrengthChanged(any());
+ verify(mSST).unregisterForServiceStateChanged(any());
+
+ clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);
+ // Test Single -> Dual
+ mAutoDataSwitchControllerUT.onMultiSimConfigChanged(2);
+
+ verify(mDisplayInfoController).registerForTelephonyDisplayInfoChanged(any(),
+ eq(EVENT_DISPLAY_INFO_CHANGED), eq(PHONE_2));
+ verify(mSignalStrengthController).registerForSignalStrengthChanged(any(),
+ eq(EVENT_SIGNAL_STRENGTH_CHANGED), eq(PHONE_2));
+ verify(mSST).registerForServiceStateChanged(any(),
+ eq(EVENT_SERVICE_STATE_CHANGED), eq(PHONE_2));
+ }
+
+ /**
+ * Trigger conditions
+ * 1. service state changes
+ * 2. data setting changes
+ * - user toggle data
+ * - user toggle auto switch feature
+ * 3. default network changes
+ * - current network lost
+ * - network become active on non-cellular network
+ */
+ private void prepareIdealUsesNonDdsCondition() {
+ // 1. service state changes
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+
+ // 2.1 User data enabled on primary SIM
+ doReturn(true).when(mPhone).isUserDataEnabled();
+
+ // 2.2 Auto switch feature is enabled
+ doReturn(true).when(mPhone2).isDataAllowed();
+
+ // 3.1 No default network
+ mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(null /*networkCapabilities*/);
+ }
+
+ private void serviceStateChanged(int phoneId,
+ @NetworkRegistrationInfo.RegistrationState int dataRegState) {
+
+ ServiceState ss = new ServiceState();
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(dataRegState)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .build());
+
+ ss.setDataRoamingFromRegistration(dataRegState
+ == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+
+ doReturn(ss).when(mPhones[phoneId]).getServiceState();
+
+ Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
+ msg.obj = new AsyncResult(phoneId, null, null);
+ mAutoDataSwitchControllerUT.sendMessage(msg);
+ }
+ private void setDefaultDataSubId(int defaultDataSub) {
+ mDefaultDataSub = defaultDataSub;
+ doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
+ }
+}