diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 8206223689c..09be2f28d22 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -241,11 +241,13 @@ settings:controller="com.android.settings.network.telephony.CarrierPreferenceController"> - + settings:controller="com.android.settings.network.telephony.Enable2gPreferenceController" + settings:useAdminDisabledSummary="true" + settings:userRestriction="no_cellular_2g"/> Radio HAL version 1.6 or greater * */ - @Override - public void updateState(Preference preference) { - super.updateState(preference); - if (preference == null || !SubscriptionManager.isUsableSubscriptionId(mSubId)) { - return; - } - final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(mSubId); - boolean isDisabledByCarrier = - carrierConfig != null - && carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENABLE_2G); - preference.setEnabled(!isDisabledByCarrier); - String summary; - if (isDisabledByCarrier) { - summary = mContext.getString(R.string.enable_2g_summary_disabled_carrier, - getCarrierName()); - } else { - summary = mContext.getString(R.string.enable_2g_summary); - } - preference.setSummary(summary); - } - - private String getCarrierName() { - SubscriptionInfo subInfo = SubscriptionUtil.getSubById(mSubscriptionManager, mSubId); - if (subInfo == null) { - return ""; - } - CharSequence carrierName = subInfo.getCarrierName(); - return TextUtils.isEmpty(carrierName) ? "" : carrierName.toString(); - } - @Override public int getAvailabilityStatus(int subId) { final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId); @@ -143,9 +160,9 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr } boolean visible = SubscriptionManager.isUsableSubscriptionId(subId) - && carrierConfig != null - && mTelephonyManager.isRadioInterfaceCapabilitySupported( - mTelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK); + && carrierConfig != null + && mTelephonyManager.isRadioInterfaceCapabilitySupported( + mTelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK); return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @@ -158,6 +175,14 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr */ @Override public boolean isChecked() { + // If an enterprise admin has disabled 2g, we show the toggle as not checked to avoid + // user confusion of seeing a checked toggle, but having 2g actually disabled. + // The RestrictedSwitchPreference will take care of transparently informing the user that + // the setting was disabled by their admin + if (isDisabledByAdmin()) { + return false; + } + long currentlyAllowedNetworkTypes = mTelephonyManager.getAllowedNetworkTypesForReason( mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G); return (currentlyAllowedNetworkTypes & BITMASK_2G) != 0; @@ -176,6 +201,10 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr */ @Override public boolean setChecked(boolean isChecked) { + if (isDisabledByAdmin()) { + return false; + } + if (!SubscriptionManager.isUsableSubscriptionId(mSubId)) { return false; } @@ -199,4 +228,8 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr mContext, SettingsEnums.ACTION_2G_ENABLED, isChecked); return true; } + + private boolean isDisabledByAdmin() { + return (mRestrictedPreference != null && mRestrictedPreference.isDisabledByAdmin()); + } } diff --git a/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java index 850ebc1d7a7..22f2d3a3bb3 100644 --- a/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java @@ -27,15 +27,19 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.os.Looper; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.network.CarrierConfigCache; +import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; import org.junit.Test; @@ -46,6 +50,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public final class Enable2gPreferenceControllerTest { private static final int SUB_ID = 2; + private static final String PREFERENCE_KEY = "TEST_2G_PREFERENCE"; @Mock private CarrierConfigCache mCarrierConfigCache; @@ -54,12 +59,18 @@ public final class Enable2gPreferenceControllerTest { @Mock private TelephonyManager mInvalidTelephonyManager; + private RestrictedSwitchPreference mPreference; + private PreferenceScreen mPreferenceScreen; private PersistableBundle mPersistableBundle; private Enable2gPreferenceController mController; private Context mContext; @Before public void setUp() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + MockitoAnnotations.initMocks(this); mContext = spy(ApplicationProvider.getApplicationContext()); @@ -75,7 +86,12 @@ public final class Enable2gPreferenceControllerTest { doReturn(mPersistableBundle).when(mCarrierConfigCache).getConfigForSubId(SUB_ID); doReturn(mPersistableBundle).when(mCarrierConfigCache).getConfigForSubId( SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mController = new Enable2gPreferenceController(mContext, "mobile_data"); + mController = new Enable2gPreferenceController(mContext, PREFERENCE_KEY); + + mPreference = spy(new RestrictedSwitchPreference(mContext)); + mPreference.setKey(PREFERENCE_KEY); + mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext); + mPreferenceScreen.addPreference(mPreference); mController.init(SUB_ID); } @@ -137,13 +153,15 @@ public final class Enable2gPreferenceControllerTest { assertThat(mController.setChecked(false)).isFalse(); } + @Test + public void setChecked_disabledByAdmin_returnFalse() { + when2gIsDisabledByAdmin(true); + assertThat(mController.setChecked(false)).isFalse(); + } + @Test public void onPreferenceChange_update() { - // Set "Enable 2G" flag to "on" - when(mTelephonyManager.getAllowedNetworkTypesForReason( - TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G)).thenReturn( - (long) (TelephonyManager.NETWORK_TYPE_BITMASK_GSM - | TelephonyManager.NETWORK_TYPE_BITMASK_LTE)); + when2gIsEnabledForReasonEnable2g(); // Setup state to allow disabling doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported( @@ -159,4 +177,42 @@ public final class Enable2gPreferenceControllerTest { TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, TelephonyManager.NETWORK_TYPE_BITMASK_LTE); } + + @Test + public void disabledByAdmin_toggleUnchecked() { + when2gIsEnabledForReasonEnable2g(); + when2gIsDisabledByAdmin(true); + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void userRestrictionInactivated_userToggleMaintainsState() { + // Initially, 2g is enabled + when2gIsEnabledForReasonEnable2g(); + when2gIsDisabledByAdmin(false); + assertThat(mController.isChecked()).isTrue(); + + // When we disable the preference by an admin, the preference should be unchecked + when2gIsDisabledByAdmin(true); + assertThat(mController.isChecked()).isFalse(); + + // If the preference is re-enabled by an admin, former state should hold + when2gIsDisabledByAdmin(false); + assertThat(mController.isChecked()).isTrue(); + } + + private void when2gIsEnabledForReasonEnable2g() { + when(mTelephonyManager.getAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G)).thenReturn( + (long) (TelephonyManager.NETWORK_TYPE_BITMASK_GSM + | TelephonyManager.NETWORK_TYPE_BITMASK_LTE)); + } + + private void when2gIsDisabledByAdmin(boolean is2gDisabledByAdmin) { + // Our controller depends on state being initialized when the associated preference is + // displayed because the admin disablement functionality flows from the association of a + // Preference with the PreferenceScreen + when(mPreference.isDisabledByAdmin()).thenReturn(is2gDisabledByAdmin); + mController.displayPreference(mPreferenceScreen); + } }