Loading src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java +88 −9 Original line number Diff line number Diff line Loading @@ -22,8 +22,10 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; Loading @@ -36,6 +38,8 @@ import com.android.settings.datausage.DataUsageUtils; import com.android.settings.network.MobileDataContentObserver; import com.android.settings.network.SubscriptionsChangeListener; import java.util.List; /** * Controls whether switch mobile data to the non-default SIM if the non-default SIM has better * availability. Loading @@ -45,39 +49,88 @@ import com.android.settings.network.SubscriptionsChangeListener; * signal/connectivity. * If this feature is enabled, data will be temporarily enabled on the non-default data SIM, * including during any voice calls. * * Showing this preference in the default data sim UI. */ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenceController implements LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient { private static final String TAG = "AutoDataSwitchPreferenceController"; private SwitchPreference mPreference; private SubscriptionsChangeListener mChangeListener; private TelephonyManager mManager; private MobileDataContentObserver mMobileDataContentObserver; private PreferenceScreen mScreen; private SubscriptionManager mSubscriptionManager; private List<SubscriptionInfo> mSubInfoList; public AutoDataSwitchPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); } void init(int subId) { this.mSubId = subId; mManager = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId); if (renewSubscriptionInfoList()) { // If the subscriptionInfos are changed, then mManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(getNonDdsSubId()); } if (mMobileDataContentObserver == null) { mMobileDataContentObserver = new MobileDataContentObserver( new Handler(Looper.getMainLooper())); mMobileDataContentObserver.setOnMobileDataChangedListener(() -> { mManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(getNonDdsSubId()); refreshPreference(); }); } } private void renewTelephonyComponent() { if (renewSubscriptionInfoList()) { // If the subscriptionInfos are changed, then if (mMobileDataContentObserver != null) { mMobileDataContentObserver.unRegister(mContext); } } if (mSubInfoList == null) { Log.d(TAG, "mSubInfoList is null. Stop to register the listener"); return; } if (mMobileDataContentObserver != null) { for (SubscriptionInfo subInfo : mSubInfoList) { mMobileDataContentObserver.register(mContext, subInfo.getSubscriptionId()); } } mManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(getNonDdsSubId()); } /** * Renew the subscriptionInfoList if the subscriptionInfos are changed. * @return true if the subscriptionInfos are changed. Otherwise, return false. */ private boolean renewSubscriptionInfoList() { final List<SubscriptionInfo> newSubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); if ((newSubInfoList == null && mSubInfoList == null) || (mSubInfoList != null && mSubInfoList.equals(newSubInfoList))) { return false; } mSubInfoList = newSubInfoList; return true; } @OnLifecycleEvent(ON_RESUME) public void onResume() { renewTelephonyComponent(); if (mChangeListener == null) { mChangeListener = new SubscriptionsChangeListener(mContext, this); } mChangeListener.start(); if (mMobileDataContentObserver == null) { mMobileDataContentObserver = new MobileDataContentObserver( new Handler(Looper.getMainLooper())); mMobileDataContentObserver.setOnMobileDataChangedListener(() -> refreshPreference()); } mMobileDataContentObserver.register(mContext, mSubId); } @OnLifecycleEvent(ON_PAUSE) Loading Loading @@ -105,6 +158,10 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc @Override public boolean setChecked(boolean isChecked) { if (mManager == null) { Log.d(TAG, "mManager is null."); return false; } mManager.setMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, isChecked); Loading @@ -119,7 +176,8 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc @Override public int getAvailabilityStatus(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId) || SubscriptionManager.getDefaultDataSubscriptionId() == subId || SubscriptionManager.getDefaultDataSubscriptionId() != subId || !SubscriptionManager.isValidSubscriptionId(getNonDdsSubId()) || (!hasMobileData())) { return CONDITIONALLY_UNAVAILABLE; } Loading @@ -136,10 +194,12 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc } @Override public void onAirplaneModeChanged(boolean airplaneModeEnabled) {} public void onAirplaneModeChanged(boolean airplaneModeEnabled) { } @Override public void onSubscriptionsChanged() { renewTelephonyComponent(); updateState(mPreference); } Loading @@ -152,4 +212,23 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc super.displayPreference(mScreen); } } private int getNonDdsSubId() { int ddsSubId = SubscriptionManager.getDefaultDataSubscriptionId(); Log.d(TAG, "DDS SubId: " + ddsSubId); if (ddsSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } if (mSubInfoList == null) { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } return mSubInfoList.stream() .mapToInt(subInfo -> subInfo.getSubscriptionId()) .filter(subId -> subId != ddsSubId) .findFirst() .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID); } } tests/robotests/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceControllerTest.java +37 −16 Original line number Diff line number Diff line Loading @@ -28,13 +28,18 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.content.Context; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -56,10 +61,15 @@ public class AutoDataSwitchPreferenceControllerTest { private TelephonyManager mTelephonyManager; @Mock private PreferenceScreen mPreferenceScreen; @Mock private SubscriptionInfo mSubscriptionInfo1; @Mock private SubscriptionInfo mSubscriptionInfo2; private Context mContext; private SwitchPreference mSwitchPreference; private AutoDataSwitchPreferenceController mController; private ShadowSubscriptionManager mShadowSubscriptionManager; @Before public void setUp() { Loading @@ -69,6 +79,11 @@ public class AutoDataSwitchPreferenceControllerTest { when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); mSwitchPreference = new SwitchPreference(mContext); when(mPreferenceScreen.findPreference(PREF_KEY)).thenReturn(mSwitchPreference); when(mSubscriptionInfo1.getSubscriptionId()).thenReturn(SUB_ID_1); when(mSubscriptionInfo2.getSubscriptionId()).thenReturn(SUB_ID_2); mShadowSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class)); mShadowSubscriptionManager.setActiveSubscriptionInfoList(ImmutableList.of( mSubscriptionInfo1, mSubscriptionInfo2)); mController = new AutoDataSwitchPreferenceController(mContext, PREF_KEY) { @Override protected boolean hasMobileData() { Loading @@ -90,66 +105,72 @@ public class AutoDataSwitchPreferenceControllerTest { } @Test public void displayPreference_defaultForData_notAvailable() { public void displayPreference_defaultForData_available() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); } @Test public void displayPreference_notDefaultForData_available() { public void displayPreference_notDefaultForData_notAvailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_2); mController.displayPreference(mPreferenceScreen); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); } @Test public void onSubscriptionsChanged_becomesDefaultForData_notAvailable() { public void onSubscriptionsChanged_becomesDefaultForData_available() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_2); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); } @Test public void onSubscriptionsChanged_noLongerDefaultForData_available() { public void onSubscriptionsChanged_noLongerDefaultForData_notAvailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_2); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); } @Test public void getAvailabilityStatus_mobileDataChangWithDefaultDataSubId_returnUnavailable() { public void getAvailabilityStatus_mobileDataChangWithDefaultDataSubId_returnAvailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.refreshPreference(); assertThat(mController.getAvailabilityStatus(SUB_ID_1)) .isEqualTo(CONDITIONALLY_UNAVAILABLE); .isEqualTo(AVAILABLE); } @Test public void getAvailabilityStatus_mobileDataChangWithoutDefaultDataSubId_returnAvailable() { public void getAvailabilityStatus_mobileDataChangWithoutDefaultDataSubId_returnUnavailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); mController.refreshPreference(); assertThat(mController.getAvailabilityStatus(SUB_ID_2)).isEqualTo(AVAILABLE); assertThat(mController.getAvailabilityStatus(SUB_ID_2)).isEqualTo( CONDITIONALLY_UNAVAILABLE); } } Loading
src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java +88 −9 Original line number Diff line number Diff line Loading @@ -22,8 +22,10 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; Loading @@ -36,6 +38,8 @@ import com.android.settings.datausage.DataUsageUtils; import com.android.settings.network.MobileDataContentObserver; import com.android.settings.network.SubscriptionsChangeListener; import java.util.List; /** * Controls whether switch mobile data to the non-default SIM if the non-default SIM has better * availability. Loading @@ -45,39 +49,88 @@ import com.android.settings.network.SubscriptionsChangeListener; * signal/connectivity. * If this feature is enabled, data will be temporarily enabled on the non-default data SIM, * including during any voice calls. * * Showing this preference in the default data sim UI. */ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenceController implements LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient { private static final String TAG = "AutoDataSwitchPreferenceController"; private SwitchPreference mPreference; private SubscriptionsChangeListener mChangeListener; private TelephonyManager mManager; private MobileDataContentObserver mMobileDataContentObserver; private PreferenceScreen mScreen; private SubscriptionManager mSubscriptionManager; private List<SubscriptionInfo> mSubInfoList; public AutoDataSwitchPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); } void init(int subId) { this.mSubId = subId; mManager = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId); if (renewSubscriptionInfoList()) { // If the subscriptionInfos are changed, then mManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(getNonDdsSubId()); } if (mMobileDataContentObserver == null) { mMobileDataContentObserver = new MobileDataContentObserver( new Handler(Looper.getMainLooper())); mMobileDataContentObserver.setOnMobileDataChangedListener(() -> { mManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(getNonDdsSubId()); refreshPreference(); }); } } private void renewTelephonyComponent() { if (renewSubscriptionInfoList()) { // If the subscriptionInfos are changed, then if (mMobileDataContentObserver != null) { mMobileDataContentObserver.unRegister(mContext); } } if (mSubInfoList == null) { Log.d(TAG, "mSubInfoList is null. Stop to register the listener"); return; } if (mMobileDataContentObserver != null) { for (SubscriptionInfo subInfo : mSubInfoList) { mMobileDataContentObserver.register(mContext, subInfo.getSubscriptionId()); } } mManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(getNonDdsSubId()); } /** * Renew the subscriptionInfoList if the subscriptionInfos are changed. * @return true if the subscriptionInfos are changed. Otherwise, return false. */ private boolean renewSubscriptionInfoList() { final List<SubscriptionInfo> newSubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); if ((newSubInfoList == null && mSubInfoList == null) || (mSubInfoList != null && mSubInfoList.equals(newSubInfoList))) { return false; } mSubInfoList = newSubInfoList; return true; } @OnLifecycleEvent(ON_RESUME) public void onResume() { renewTelephonyComponent(); if (mChangeListener == null) { mChangeListener = new SubscriptionsChangeListener(mContext, this); } mChangeListener.start(); if (mMobileDataContentObserver == null) { mMobileDataContentObserver = new MobileDataContentObserver( new Handler(Looper.getMainLooper())); mMobileDataContentObserver.setOnMobileDataChangedListener(() -> refreshPreference()); } mMobileDataContentObserver.register(mContext, mSubId); } @OnLifecycleEvent(ON_PAUSE) Loading Loading @@ -105,6 +158,10 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc @Override public boolean setChecked(boolean isChecked) { if (mManager == null) { Log.d(TAG, "mManager is null."); return false; } mManager.setMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, isChecked); Loading @@ -119,7 +176,8 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc @Override public int getAvailabilityStatus(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId) || SubscriptionManager.getDefaultDataSubscriptionId() == subId || SubscriptionManager.getDefaultDataSubscriptionId() != subId || !SubscriptionManager.isValidSubscriptionId(getNonDdsSubId()) || (!hasMobileData())) { return CONDITIONALLY_UNAVAILABLE; } Loading @@ -136,10 +194,12 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc } @Override public void onAirplaneModeChanged(boolean airplaneModeEnabled) {} public void onAirplaneModeChanged(boolean airplaneModeEnabled) { } @Override public void onSubscriptionsChanged() { renewTelephonyComponent(); updateState(mPreference); } Loading @@ -152,4 +212,23 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc super.displayPreference(mScreen); } } private int getNonDdsSubId() { int ddsSubId = SubscriptionManager.getDefaultDataSubscriptionId(); Log.d(TAG, "DDS SubId: " + ddsSubId); if (ddsSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } if (mSubInfoList == null) { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } return mSubInfoList.stream() .mapToInt(subInfo -> subInfo.getSubscriptionId()) .filter(subId -> subId != ddsSubId) .findFirst() .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID); } }
tests/robotests/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceControllerTest.java +37 −16 Original line number Diff line number Diff line Loading @@ -28,13 +28,18 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.content.Context; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -56,10 +61,15 @@ public class AutoDataSwitchPreferenceControllerTest { private TelephonyManager mTelephonyManager; @Mock private PreferenceScreen mPreferenceScreen; @Mock private SubscriptionInfo mSubscriptionInfo1; @Mock private SubscriptionInfo mSubscriptionInfo2; private Context mContext; private SwitchPreference mSwitchPreference; private AutoDataSwitchPreferenceController mController; private ShadowSubscriptionManager mShadowSubscriptionManager; @Before public void setUp() { Loading @@ -69,6 +79,11 @@ public class AutoDataSwitchPreferenceControllerTest { when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); mSwitchPreference = new SwitchPreference(mContext); when(mPreferenceScreen.findPreference(PREF_KEY)).thenReturn(mSwitchPreference); when(mSubscriptionInfo1.getSubscriptionId()).thenReturn(SUB_ID_1); when(mSubscriptionInfo2.getSubscriptionId()).thenReturn(SUB_ID_2); mShadowSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class)); mShadowSubscriptionManager.setActiveSubscriptionInfoList(ImmutableList.of( mSubscriptionInfo1, mSubscriptionInfo2)); mController = new AutoDataSwitchPreferenceController(mContext, PREF_KEY) { @Override protected boolean hasMobileData() { Loading @@ -90,66 +105,72 @@ public class AutoDataSwitchPreferenceControllerTest { } @Test public void displayPreference_defaultForData_notAvailable() { public void displayPreference_defaultForData_available() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); } @Test public void displayPreference_notDefaultForData_available() { public void displayPreference_notDefaultForData_notAvailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_2); mController.displayPreference(mPreferenceScreen); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); } @Test public void onSubscriptionsChanged_becomesDefaultForData_notAvailable() { public void onSubscriptionsChanged_becomesDefaultForData_available() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_2); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); } @Test public void onSubscriptionsChanged_noLongerDefaultForData_available() { public void onSubscriptionsChanged_noLongerDefaultForData_notAvailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_2); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isTrue(); assertThat(mSwitchPreference.isVisible()).isTrue(); assertThat(mController.isAvailable()).isFalse(); assertThat(mSwitchPreference.isVisible()).isFalse(); } @Test public void getAvailabilityStatus_mobileDataChangWithDefaultDataSubId_returnUnavailable() { public void getAvailabilityStatus_mobileDataChangWithDefaultDataSubId_returnAvailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.refreshPreference(); assertThat(mController.getAvailabilityStatus(SUB_ID_1)) .isEqualTo(CONDITIONALLY_UNAVAILABLE); .isEqualTo(AVAILABLE); } @Test public void getAvailabilityStatus_mobileDataChangWithoutDefaultDataSubId_returnAvailable() { public void getAvailabilityStatus_mobileDataChangWithoutDefaultDataSubId_returnUnavailable() { ShadowSubscriptionManager.setDefaultDataSubscriptionId(SUB_ID_1); mController.init(SUB_ID_1); mController.displayPreference(mPreferenceScreen); mController.refreshPreference(); assertThat(mController.getAvailabilityStatus(SUB_ID_2)).isEqualTo(AVAILABLE); assertThat(mController.getAvailabilityStatus(SUB_ID_2)).isEqualTo( CONDITIONALLY_UNAVAILABLE); } }