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

Commit 9ca87091 authored by tomhsu's avatar tomhsu
Browse files

Disabled Settings preference in case Satellite's conditions.

 Conditions
   - Satellite session started
   - Current subscription for Satellite is carrier based.
 Target preference UI in android settings
   - preferred network type
   - Automatically select network

Flag: com.android.settings.flags.satellite_oem_settings_ux_migration
Fix: b/378409439
Fix: b/378409428
Test: atest pass
Test: Manual test pass
Change-Id: I7aa04b818c8866bf5c891c28372a249c964b066f
parent 01043fdf
Loading
Loading
Loading
Loading
+83 −12
Original line number Diff line number Diff line
@@ -16,9 +16,6 @@

package com.android.settings.network.telephony;

import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;

import static com.android.settings.network.telephony.EnabledNetworkModePreferenceControllerHelperKt.getNetworkModePreferenceType;
import static com.android.settings.network.telephony.EnabledNetworkModePreferenceControllerHelperKt.setAllowedNetworkTypes;
import static com.android.settings.network.telephony.mode.NetworkModes.addNrToLteNetworkMode;
@@ -33,15 +30,17 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.satellite.SatelliteManager;
import android.telephony.satellite.SatelliteModemStateCallback;
import android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.ListPreference;
import androidx.preference.ListPreferenceDialogFragmentCompat;
import androidx.preference.Preference;
@@ -67,7 +66,7 @@ import java.util.stream.Stream;
 */
public class EnabledNetworkModePreferenceController extends
        BasePreferenceController implements
        ListPreference.OnPreferenceChangeListener, LifecycleObserver,
        ListPreference.OnPreferenceChangeListener, DefaultLifecycleObserver,
        SubscriptionsChangeListener.SubscriptionsChangeListenerClient {

    private static final String LOG_TAG = "EnabledNetworkMode";
@@ -83,6 +82,43 @@ public class EnabledNetworkModePreferenceController extends
    private PhoneCallStateTelephonyCallback mTelephonyCallback;
    private FragmentManager mFragmentManager;
    private LifecycleOwner mViewLifecycleOwner;
    private SatelliteManager mSatelliteManager;
    private boolean mIsSatelliteSessionStarted = false;
    private boolean mIsCurrentSubscriptionForSatellite = false;

    @VisibleForTesting
    final SelectedNbIotSatelliteSubscriptionCallback mSelectedNbIotSatelliteSubscriptionCallback =
            new SelectedNbIotSatelliteSubscriptionCallback() {
                @Override
                public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) {
                    mIsCurrentSubscriptionForSatellite = selectedSubId == mSubId;
                    updatePreference();
                }
            };

    @VisibleForTesting
    final SatelliteModemStateCallback mSatelliteModemStateCallback =
            new SatelliteModemStateCallback() {
                @Override
                public void onSatelliteModemStateChanged(int state) {
                    switch (state) {
                        case SatelliteManager.SATELLITE_MODEM_STATE_OFF:
                        case SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE:
                        case SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN:
                            if (mIsSatelliteSessionStarted) {
                                mIsSatelliteSessionStarted = false;
                                updatePreference();
                            }
                            break;
                        default:
                            if (!mIsSatelliteSessionStarted) {
                                mIsSatelliteSessionStarted = true;
                                updatePreference();
                            }
                            break;
                    }
                }
            };

    public EnabledNetworkModePreferenceController(Context context, String key) {
        super(context, key);
@@ -90,6 +126,7 @@ public class EnabledNetworkModePreferenceController extends
        if (mTelephonyCallback == null) {
            mTelephonyCallback = new PhoneCallStateTelephonyCallback();
        }
        mSatelliteManager = context.getSystemService(SatelliteManager.class);
    }

    @Override
@@ -103,8 +140,22 @@ public class EnabledNetworkModePreferenceController extends
        return mCallState == TelephonyManager.CALL_STATE_IDLE;
    }

    @OnLifecycleEvent(ON_START)
    public void onStart() {
    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        if (com.android.settings.flags.Flags.satelliteOemSettingsUxMigration()) {
            if (mSatelliteManager != null) {
                try {
                    mSatelliteManager.registerForModemStateChanged(
                            mContext.getMainExecutor(), mSatelliteModemStateCallback);
                    mSatelliteManager.registerForSelectedNbIotSatelliteSubscriptionChanged(
                            mContext.getMainExecutor(),
                            mSelectedNbIotSatelliteSubscriptionCallback);
                } catch (IllegalStateException e) {
                    Log.w(LOG_TAG, "IllegalStateException : " + e);
                }
            }
        }

        mSubscriptionsListener.start();
        if (mAllowedNetworkTypesListener == null || mTelephonyCallback == null) {
            return;
@@ -113,9 +164,21 @@ public class EnabledNetworkModePreferenceController extends
        mTelephonyCallback.register(mTelephonyManager, mSubId);
    }

    @OnLifecycleEvent(ON_STOP)
    public void onStop() {
    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        mSubscriptionsListener.stop();
        if (com.android.settings.flags.Flags.satelliteOemSettingsUxMigration()) {
            if (mSatelliteManager != null) {
                try {
                    mSatelliteManager.unregisterForModemStateChanged(mSatelliteModemStateCallback);
                    mSatelliteManager.unregisterForSelectedNbIotSatelliteSubscriptionChanged(
                            mSelectedNbIotSatelliteSubscriptionCallback);
                } catch (IllegalStateException e) {
                    Log.w(LOG_TAG, "IllegalStateException : " + e);
                }
            }
        }

        if (mAllowedNetworkTypesListener == null || mTelephonyCallback == null) {
            return;
        }
@@ -147,7 +210,7 @@ public class EnabledNetworkModePreferenceController extends
        listPreference.setEntryValues(mBuilder.getEntryValues());
        listPreference.setValue(Integer.toString(mBuilder.getSelectedEntryValue()));
        listPreference.setSummary(mBuilder.getSummary());
        boolean listPreferenceEnabled = isCallStateIdle();
        boolean listPreferenceEnabled = isPreferenceShallEnabled();
        listPreference.setEnabled(listPreferenceEnabled);
        if (!listPreferenceEnabled) {
            // If dialog is already opened when ListPreference disabled, dismiss them.
@@ -203,6 +266,14 @@ public class EnabledNetworkModePreferenceController extends
        }
    }

    private boolean isPreferenceShallEnabled() {
        Log.d(LOG_TAG, "isPreferenceShallEnabled, mIsSatelliteSessionStarted : "
                + mIsSatelliteSessionStarted + " / mIsCurrentSubscriptionForSatellite : "
                + mIsCurrentSubscriptionForSatellite);
        return isCallStateIdle()
                && !(mIsSatelliteSessionStarted && mIsCurrentSubscriptionForSatellite);
    }

    private final class PreferenceEntriesBuilder {
        private CarrierConfigCache mCarrierConfigCache;
        private Context mContext;
+93 −4
Original line number Diff line number Diff line
@@ -24,10 +24,18 @@ import android.telephony.CarrierConfigManager;
import android.telephony.RadioAccessFamily;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.satellite.SatelliteManager;
import android.telephony.satellite.SatelliteModemStateCallback;
import android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -38,17 +46,56 @@ import com.android.settings.network.telephony.mode.NetworkModes;
 * Preference controller for "Preferred network mode"
 */
public class PreferredNetworkModePreferenceController extends BasePreferenceController
        implements ListPreference.OnPreferenceChangeListener {
        implements ListPreference.OnPreferenceChangeListener, DefaultLifecycleObserver {
    private static final String TAG = "PrefNetworkModeCtrl";

    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    private CarrierConfigCache mCarrierConfigCache;
    private TelephonyManager mTelephonyManager;
    private boolean mIsGlobalCdma;
    private SatelliteManager mSatelliteManager;
    private Preference mPreference;
    private boolean mIsSatelliteSessionStarted = false;
    private boolean mIsCurrentSubscriptionForSatellite = false;

    @VisibleForTesting
    final SelectedNbIotSatelliteSubscriptionCallback mSelectedNbIotSatelliteSubscriptionCallback =
            new SelectedNbIotSatelliteSubscriptionCallback() {
                @Override
                public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) {
                    mIsCurrentSubscriptionForSatellite = selectedSubId == mSubId;
                    updateState(mPreference);
                }
            };

    @VisibleForTesting
    final SatelliteModemStateCallback mSatelliteModemStateCallback =
            new SatelliteModemStateCallback() {
                @Override
                public void onSatelliteModemStateChanged(int state) {
                    switch (state) {
                        case SatelliteManager.SATELLITE_MODEM_STATE_OFF:
                        case SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE:
                        case SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN:
                            if (mIsSatelliteSessionStarted) {
                                mIsSatelliteSessionStarted = false;
                                updateState(mPreference);
                            }
                            break;
                        default:
                            if (!mIsSatelliteSessionStarted) {
                                mIsSatelliteSessionStarted = true;
                                updateState(mPreference);
                            }
                            break;
                    }
                }
            };

    public PreferredNetworkModePreferenceController(Context context, String key) {
        super(context, key);
        mCarrierConfigCache = CarrierConfigCache.getInstance(context);
        mSatelliteManager = context.getSystemService(SatelliteManager.class);
    }

    @Override
@@ -58,9 +105,19 @@ public class PreferredNetworkModePreferenceController extends BasePreferenceCont
                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreference = screen.findPreference(getPreferenceKey());
    }

    @Override
    public void updateState(Preference preference) {
        if (preference == null) {
            return;
        }
        super.updateState(preference);
        preference.setEnabled(!(mIsCurrentSubscriptionForSatellite && mIsSatelliteSessionStarted));
        final ListPreference listPreference = (ListPreference) preference;
        final int networkMode = getPreferredNetworkMode();
        listPreference.setValue(Integer.toString(networkMode));
@@ -90,6 +147,38 @@ public class PreferredNetworkModePreferenceController extends BasePreferenceCont
                && carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_CDMA_CHOICES_BOOL);
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        if (com.android.settings.flags.Flags.satelliteOemSettingsUxMigration()) {
            if (mSatelliteManager != null) {
                try {
                    mSatelliteManager.registerForModemStateChanged(
                            mContext.getMainExecutor(), mSatelliteModemStateCallback);
                    mSatelliteManager.registerForSelectedNbIotSatelliteSubscriptionChanged(
                            mContext.getMainExecutor(),
                            mSelectedNbIotSatelliteSubscriptionCallback);
                } catch (IllegalStateException e) {
                    Log.w(TAG, "IllegalStateException : " + e);
                }
            }
        }
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        if (com.android.settings.flags.Flags.satelliteOemSettingsUxMigration()) {
            if (mSatelliteManager != null) {
                try {
                    mSatelliteManager.unregisterForModemStateChanged(mSatelliteModemStateCallback);
                    mSatelliteManager.unregisterForSelectedNbIotSatelliteSubscriptionChanged(
                            mSelectedNbIotSatelliteSubscriptionCallback);
                } catch (IllegalStateException e) {
                    Log.w(TAG, "IllegalStateException : " + e);
                }
            }
        }
    }

    private int getPreferredNetworkMode() {
        if (mTelephonyManager == null) {
            Log.w(TAG, "TelephonyManager is null");
+66 −7
Original line number Diff line number Diff line
@@ -25,18 +25,22 @@ import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.satellite.SatelliteManager
import android.telephony.satellite.SatelliteModemStateCallback
import android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.Settings.NetworkSelectActivity
import com.android.settings.flags.Flags
import com.android.settings.network.CarrierConfigCache
import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settings.network.telephony.allowedNetworkTypesFlow
@@ -46,8 +50,6 @@ import com.android.settingslib.spa.framework.compose.OverridableFlow
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlin.properties.Delegates.notNull
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
@@ -59,6 +61,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.time.Duration.Companion.seconds

/**
 * Preference controller for "Auto Select Network"
@@ -73,9 +76,13 @@ class AutoSelectPreferenceController @JvmOverloads constructor(
    private val getConfigForSubId: (subId: Int) -> PersistableBundle = { subId ->
        CarrierConfigCache.getInstance(context).getConfigForSubId(subId)
    },
) : ComposePreferenceController(context, key) {
) : ComposePreferenceController(context, key), DefaultLifecycleObserver {

    private var isSatelliteSessionStarted = false
    private var isSelectedSubIdForSatellite = false

    private lateinit var telephonyManager: TelephonyManager
    private lateinit var satelliteManager: SatelliteManager
    private val listeners = mutableListOf<OnNetworkSelectModeListener>()

    @VisibleForTesting
@@ -83,6 +90,21 @@ class AutoSelectPreferenceController @JvmOverloads constructor(

    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID

    val satelliteModemStateCallback = SatelliteModemStateCallback { state ->
        isSatelliteSessionStarted = when (state) {
            SatelliteManager.SATELLITE_MODEM_STATE_OFF,
            SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE,
            SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN -> false

            else -> true
        }
    }

    val selectedNbIotSatelliteSubscriptionCallback =
        SelectedNbIotSatelliteSubscriptionCallback { selectedSubId ->
            isSelectedSubIdForSatellite = selectedSubId == subId
        }

    /**
     * Initialization based on given subscription id.
     */
@@ -90,7 +112,7 @@ class AutoSelectPreferenceController @JvmOverloads constructor(
        this.subId = subId
        telephonyManager = mContext.getSystemService(TelephonyManager::class.java)!!
            .createForSubscriptionId(subId)

        satelliteManager = mContext.getSystemService(SatelliteManager::class.java)!!
        return this
    }

@@ -117,7 +139,10 @@ class AutoSelectPreferenceController @JvmOverloads constructor(
        SwitchPreference(object : SwitchPreferenceModel {
            override val title = stringResource(R.string.select_automatically)
            override val summary = { disallowedSummary }
            override val changeable = { disallowedSummary.isEmpty() }
            override val changeable = {
                disallowedSummary.isEmpty()
                        && !(isSatelliteSessionStarted && isSelectedSubIdForSatellite)
            }
            override val checked = { isAuto }
            override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
                if (newChecked) {
@@ -132,6 +157,38 @@ class AutoSelectPreferenceController @JvmOverloads constructor(
        })
    }

    override fun onStart(owner: LifecycleOwner) {
        if (Flags.satelliteOemSettingsUxMigration()) {
            if (satelliteManager != null) {
                try {
                    satelliteManager.registerForModemStateChanged(
                        mContext.mainExecutor, satelliteModemStateCallback
                    )
                    satelliteManager.registerForSelectedNbIotSatelliteSubscriptionChanged(
                        mContext.mainExecutor, selectedNbIotSatelliteSubscriptionCallback
                    )
                } catch (e: IllegalStateException) {
                    Log.w(TAG, "IllegalStateException $e")
                }
            }
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        if (Flags.satelliteOemSettingsUxMigration()) {
            if (satelliteManager != null) {
                try {
                    satelliteManager.unregisterForModemStateChanged(satelliteModemStateCallback)
                    satelliteManager.unregisterForSelectedNbIotSatelliteSubscriptionChanged(
                        selectedNbIotSatelliteSubscriptionCallback
                    )
                } catch (e: IllegalStateException) {
                    Log.w(TAG, "IllegalStateException $e")
                }
            }
        }
    }

    private suspend fun getDisallowedSummary(serviceState: ServiceState): String =
        withContext(Dispatchers.Default) {
            if (!serviceState.roaming && onlyAutoSelectInHome()) {
@@ -213,6 +270,8 @@ class AutoSelectPreferenceController @JvmOverloads constructor(
    }

    companion object {
        private const val TAG = "AutoSelectPreferenceController"

        private val MINIMUM_DIALOG_TIME = 1.seconds
    }
}
+60 −1
Original line number Diff line number Diff line
@@ -18,10 +18,12 @@ package com.android.settings.network.telephony.gsm

import android.content.Context
import android.content.Intent
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
import android.telephony.TelephonyManager
import android.telephony.satellite.SatelliteManager
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
@@ -36,6 +38,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.Settings.NetworkSelectActivity
import com.android.settings.flags.Flags
import com.android.settings.spa.preference.ComposePreference
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
@@ -46,6 +49,8 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doNothing
@@ -57,6 +62,9 @@ import org.mockito.kotlin.whenever

@RunWith(AndroidJUnit4::class)
class AutoSelectPreferenceControllerTest {
    @get:Rule
    val mockito = MockitoJUnit.rule()

    @get:Rule
    val composeTestRule = createComposeRule()

@@ -65,8 +73,12 @@ class AutoSelectPreferenceControllerTest {
        on { simOperatorName } doReturn OPERATOR_NAME
    }

    private val mockSatelliteManager = mock<SatelliteManager> {
    }

    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
        on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
        on { getSystemService(SatelliteManager::class.java) } doReturn mockSatelliteManager
        doNothing().whenever(mock).startActivity(any())
    }

@@ -115,7 +127,6 @@ class AutoSelectPreferenceControllerTest {
            .assertIsOff()
    }


    @Test
    fun isEnabled_isRoaming_enabled() {
        serviceState.roaming = true
@@ -158,6 +169,54 @@ class AutoSelectPreferenceControllerTest {
            .assertIsNotEnabled()
    }

    @Test
    @EnableFlags(Flags.FLAG_SATELLITE_OEM_SETTINGS_UX_MIGRATION)
    fun isEnabled_isSatelliteSessionStartedAndSelectedSubForSatellite_disabled() {
        controller.selectedNbIotSatelliteSubscriptionCallback
            .onSelectedNbIotSatelliteSubscriptionChanged(SUB_ID)
        controller.satelliteModemStateCallback
            .onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED)

        composeTestRule.setContent {
            controller.Content()
        }

        composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
            .assertIsNotEnabled()
    }

    @Test
    @EnableFlags(Flags.FLAG_SATELLITE_OEM_SETTINGS_UX_MIGRATION)
    fun isEnabled_isSatelliteSessionNotStartedButIsSelectedSubForSatellite_enabled() {
        controller.selectedNbIotSatelliteSubscriptionCallback
            .onSelectedNbIotSatelliteSubscriptionChanged(SUB_ID)
        controller.satelliteModemStateCallback
            .onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_OFF)

        composeTestRule.setContent {
            controller.Content()
        }

        composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
            .assertIsEnabled()
    }

    @Test
    @EnableFlags(Flags.FLAG_SATELLITE_OEM_SETTINGS_UX_MIGRATION)
    fun isEnabled_isSatelliteSessionStartedButNotSelectedSubForSatellite_enabled() {
        controller.selectedNbIotSatelliteSubscriptionCallback
            .onSelectedNbIotSatelliteSubscriptionChanged(0)
        controller.satelliteModemStateCallback
            .onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED)

        composeTestRule.setContent {
            controller.Content()
        }

        composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
            .assertIsEnabled()
    }

    @Test
    fun onClick_turnOff_startNetworkSelectActivity() {
        serviceState.isManualSelection = false
+49 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading