Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2ddff38 authored by Gil Cukierman's avatar Gil Cukierman
Browse files

Honor DISALLOW_CELLULAR_2G User Restriction in Enable2gPreferenceController

When 2g is disallowed by a device admin the 2g toggle in the preference
screen will implement the following behaviors as a result of being
changed to a RestrictedSwitchPreference:
1. become disabled (greyed out)
2. show a summary message explaining that the preference was disabled by
   an admin
3 show a pop up when a user tries to click on the preference informing
  them that the setting is unusable because it was disabled by an admin.

Additionally, the toggle will show as unchecked (off) when admins
disable 2g.

When 2g is re-enabled by a device admin, the preference screen will go back
to its previous state.

Test: atest Enable2gPreferenceControllerTest
Bug: b/248250240
Change-Id: I7af901f2d9f62ebfe884e01724d8eff845c2968e
parent 90dcc7c5
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -241,11 +241,13 @@
            settings:controller="com.android.settings.network.telephony.CarrierPreferenceController">
        </Preference>

        <SwitchPreference
        <com.android.settingslib.RestrictedSwitchPreference
            android:key="enable_2g"
            android:title="@string/enable_2g_title"
            android:summary="@string/enable_2g_summary"
            settings:controller="com.android.settings.network.telephony.Enable2gPreferenceController" />
            settings:controller="com.android.settings.network.telephony.Enable2gPreferenceController"
            settings:useAdminDisabledSummary="true"
            settings:userRestriction="no_cellular_2g"/>

        <SwitchPreference
            android:key="nr_advanced_calling"
+61 −28
Original line number Diff line number Diff line
@@ -26,11 +26,13 @@ import android.text.TextUtils;
import android.util.Log;

import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.network.CarrierConfigCache;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;

/**
@@ -61,6 +63,7 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
    private CarrierConfigCache mCarrierConfigCache;
    private SubscriptionManager mSubscriptionManager;
    private TelephonyManager mTelephonyManager;
    private RestrictedSwitchPreference mRestrictedPreference;

    /**
     * Class constructor of "Enable 2G" toggle.
@@ -73,6 +76,7 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
        mCarrierConfigCache = CarrierConfigCache.getInstance(context);
        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
        mRestrictedPreference = null;
    }

    /**
@@ -88,28 +92,25 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
        return this;
    }

    /**
     * Get the {@link com.android.settings.core.BasePreferenceController.AvailabilityStatus} for
     * this preference given a {@code subId}.
     * <p>
     * A return value of {@link #AVAILABLE} denotes that the 2g status can be updated for this
     * particular subscription.
     * We return {@link #AVAILABLE} if the following conditions are met and {@link
     * #CONDITIONALLY_UNAVAILABLE} otherwise.
     * <ul>
     *     <li>The subscription is usable {@link SubscriptionManager#isUsableSubscriptionId}</li>
     *     <li>The carrier has not opted to disable this preference
     *     {@link CarrierConfigManager#KEY_HIDE_ENABLE_2G}</li>
     *     <li>The device supports
     *     <a href="https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/radio/1.6/IRadio.hal">Radio HAL version 1.6 or greater</a> </li>
     * </ul>
     */
    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mRestrictedPreference = screen.findPreference(getPreferenceKey());
    }

    @Override
    public void updateState(Preference preference) {
        super.updateState(preference);

        // The device admin decision overrides any carrier preferences
        if (isDisabledByAdmin()) {
            return;
        }

        if (preference == null || !SubscriptionManager.isUsableSubscriptionId(mSubId)) {
            return;
        }

        final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(mSubId);
        boolean isDisabledByCarrier =
                carrierConfig != null
@@ -134,6 +135,22 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
        return TextUtils.isEmpty(carrierName) ? "" : carrierName.toString();
    }

    /**
     * Get the {@link com.android.settings.core.BasePreferenceController.AvailabilityStatus} for
     * this preference given a {@code subId}.
     * <p>
     * A return value of {@link #AVAILABLE} denotes that the 2g status can be updated for this
     * particular subscription.
     * We return {@link #AVAILABLE} if the following conditions are met and {@link
     * #CONDITIONALLY_UNAVAILABLE} otherwise.
     * <ul>
     *     <li>The subscription is usable {@link SubscriptionManager#isUsableSubscriptionId}</li>
     *     <li>The carrier has not opted to disable this preference
     *     {@link CarrierConfigManager#KEY_HIDE_ENABLE_2G}</li>
     *     <li>The device supports
     *     <a href="https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/radio/1.6/IRadio.hal">Radio HAL version 1.6 or greater</a> </li>
     * </ul>
     */
    @Override
    public int getAvailabilityStatus(int subId) {
        final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId);
@@ -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());
    }
}
+62 −6
Original line number Diff line number Diff line
@@ -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);
    }
}