From 7b9c22348ab1c166f5b4358d36e7c7ea735aa605 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Mon, 6 Mar 2023 17:24:51 +0800 Subject: [PATCH] Avoid ANR when UI query network selection state Moving the quert network selection state to back ground and listening the service state to detect the betwork selection changed. Bug: 270652395 Test: atest AutoSelectPreferenceControllerTest Change-Id: I81a597f28cf7ce25ff4eff5100bdb4d29c897a14 (cherry picked from commit 7b80b4ecd9ed29240748da84aceb29ba2fc41bda) --- .../telephony/MobileNetworkSettings.java | 2 +- .../gsm/AutoSelectPreferenceController.java | 93 ++++++++++++++++--- .../AutoSelectPreferenceControllerTest.java | 53 ++++++++++- 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index d5b2c2e39c4..a9c92186611 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -272,7 +272,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme use(OpenNetworkSelectPagePreferenceController.class).init(mSubId); final AutoSelectPreferenceController autoSelectPreferenceController = use(AutoSelectPreferenceController.class) - .init(mSubId) + .init(getLifecycle(), mSubId) .addListener(openNetworkSelectPagePreferenceController); use(NetworkPreferenceCategoryController.class).init(mSubId) .setChildren(Arrays.asList(autoSelectPreferenceController)); diff --git a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java index 72d9e91ff77..f46a45232a7 100644 --- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java +++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java @@ -34,8 +34,10 @@ import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.util.Log; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.Preference; @@ -45,6 +47,7 @@ import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.network.AllowedNetworkTypesListener; import com.android.settings.network.CarrierConfigCache; +import com.android.settings.network.helper.ServiceStateStatus; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.TelephonyTogglePreferenceController; import com.android.settingslib.utils.ThreadUtils; @@ -62,6 +65,9 @@ import java.util.concurrent.atomic.AtomicLong; public class AutoSelectPreferenceController extends TelephonyTogglePreferenceController implements LifecycleObserver{ private static final long MINIMUM_DIALOG_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1); + private static final String LOG_TAG = "AutoSelectPreferenceController"; + private static final String INTERNAL_LOG_TAG_INIT = "Init"; + private static final String INTERNAL_LOG_TAG_AFTERSET = "AfterSet"; private final Handler mUiHandler; private PreferenceScreen mPreferenceScreen; @@ -76,6 +82,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon private AtomicBoolean mUpdatingConfig; private int mCacheOfModeStatus; private AtomicLong mRecursiveUpdate; + ServiceStateStatus mServiceStateStatus; public AutoSelectPreferenceController(Context context, String key) { super(context, key); @@ -129,12 +136,6 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon @Override public boolean isChecked() { - if (!mUpdatingConfig.get()) { - mCacheOfModeStatus = mTelephonyManager.getNetworkSelectionMode(); - for (OnNetworkSelectModeListener lsn : mListeners) { - lsn.onNetworkSelectModeUpdated(mCacheOfModeStatus); - } - } return mCacheOfModeStatus == TelephonyManager.NETWORK_SELECTION_MODE_AUTO; } @@ -197,12 +198,22 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon //Update UI in UI thread final long durationMillis = SystemClock.elapsedRealtime() - startMillis; + mUiHandler.postDelayed(() -> { - mRecursiveUpdate.getAndIncrement(); - mSwitchPreference.setEnabled(true); - mSwitchPreference.setChecked(isChecked()); - mRecursiveUpdate.decrementAndGet(); - dismissProgressBar(); + ThreadUtils.postOnBackgroundThread(() -> { + queryNetworkSelectionMode(INTERNAL_LOG_TAG_AFTERSET); + + //Update UI in UI thread + mUiHandler.post(() -> { + mRecursiveUpdate.getAndIncrement(); + if (mSwitchPreference != null) { + mSwitchPreference.setEnabled(true); + mSwitchPreference.setChecked(isChecked()); + } + mRecursiveUpdate.decrementAndGet(); + dismissProgressBar(); + }); + }); }, Math.max(MINIMUM_DIALOG_TIME_MILLIS - durationMillis, 0)); }); } @@ -210,7 +221,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon /** * Initialization based on given subscription id. **/ - public AutoSelectPreferenceController init(int subId) { + public AutoSelectPreferenceController init(Lifecycle lifecycle, int subId) { mSubId = subId; mTelephonyManager = mContext.getSystemService(TelephonyManager.class) .createForSubscriptionId(mSubId); @@ -221,6 +232,29 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL) : false; + mServiceStateStatus = new ServiceStateStatus(lifecycle, mTelephonyManager, + new HandlerExecutor(mUiHandler)) { + @Override + protected void setValue(ServiceState status) { + if (status == null) { + return; + } + updateUiAutoSelectValue(status); + } + }; + + ThreadUtils.postOnBackgroundThread(() -> { + queryNetworkSelectionMode(INTERNAL_LOG_TAG_INIT); + + //Update UI in UI thread + mUiHandler.post(() -> { + if (mSwitchPreference != null) { + mRecursiveUpdate.getAndIncrement(); + mSwitchPreference.setChecked(isChecked()); + mRecursiveUpdate.decrementAndGet(); + } + }); + }); return this; } @@ -230,6 +264,41 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon return this; } + private void queryNetworkSelectionMode(String tag) { + mCacheOfModeStatus = mTelephonyManager.getNetworkSelectionMode(); + Log.d(LOG_TAG, tag + ": query commend done. mCacheOfModeStatus: " + mCacheOfModeStatus); + updateListenerValue(); + } + + @VisibleForTesting + void updateUiAutoSelectValue(ServiceState status) { + if (status == null) { + return; + } + if (!mUpdatingConfig.get()) { + int networkSelectionMode = status.getIsManualSelection() + ? TelephonyManager.NETWORK_SELECTION_MODE_MANUAL + : TelephonyManager.NETWORK_SELECTION_MODE_AUTO; + if (mCacheOfModeStatus == networkSelectionMode) { + return; + } + mCacheOfModeStatus = networkSelectionMode; + Log.d(LOG_TAG, "updateUiAutoSelectValue: mCacheOfModeStatus: " + mCacheOfModeStatus); + updateListenerValue(); + + mRecursiveUpdate.getAndIncrement(); + updateState(mSwitchPreference); + mRecursiveUpdate.decrementAndGet(); + + } + } + + private void updateListenerValue() { + for (OnNetworkSelectModeListener lsn : mListeners) { + lsn.onNetworkSelectModeUpdated(mCacheOfModeStatus); + } + } + private void showAutoSelectProgressBar() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(mContext); diff --git a/tests/unit/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java index 292b4a9ae5c..39f2050b7f7 100644 --- a/tests/unit/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceControllerTest.java @@ -28,9 +28,11 @@ import android.app.ProgressDialog; import android.content.Context; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; +import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import androidx.lifecycle.Lifecycle; import androidx.preference.SwitchPreference; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -61,6 +63,10 @@ public class AutoSelectPreferenceControllerTest { private CarrierConfigCache mCarrierConfigCache; @Mock private ProgressDialog mProgressDialog; + @Mock + private ServiceState mTestServiceState; + @Mock + private Lifecycle mLifecycle; private PersistableBundle mCarrierConfig; private AutoSelectPreferenceController mController; @@ -88,7 +94,16 @@ public class AutoSelectPreferenceControllerTest { mController = new AutoSelectPreferenceController(mContext, "auto_select"); mController.mProgressDialog = mProgressDialog; mController.mSwitchPreference = mSwitchPreference; - mController.init(SUB_ID); + mController.init(mLifecycle, SUB_ID); + sleepAfterInit(); + } + + private void sleepAfterInit() { + try { + Thread.sleep(2000); + } catch (Exception e) { + fail("Sleep timeout " + e); + } } @Test @@ -111,7 +126,8 @@ public class AutoSelectPreferenceControllerTest { @Test public void updateState_isRoaming_enabled() { - when(mTelephonyManager.getServiceState().getRoaming()).thenReturn(true); + when(mTelephonyManager.getServiceState()).thenReturn(mTestServiceState); + when(mTestServiceState.getRoaming()).thenReturn(true); mController.updateState(mSwitchPreference); @@ -120,7 +136,8 @@ public class AutoSelectPreferenceControllerTest { @Test public void updateState_notRoamingWithAutoSelectOn_disabled() { - when(mTelephonyManager.getServiceState().getRoaming()).thenReturn(false); + when(mTelephonyManager.getServiceState()).thenReturn(mTestServiceState); + when(mTestServiceState.getRoaming()).thenReturn(false); doReturn(OPERATOR_NAME).when(mTelephonyManager).getSimOperatorName(); mController.updateState(mSwitchPreference); @@ -136,6 +153,34 @@ public class AutoSelectPreferenceControllerTest { when(mCarrierConfigCache.getConfigForSubId(SUB_ID)).thenReturn(null); // Should not crash - mController.init(SUB_ID); + mController.init(mLifecycle, SUB_ID); + } + + @Test + public void updateUiAutoSelectValue_serviceStateGetIsManualSelection_isCheckedFalse() { + when(mTelephonyManager.getNetworkSelectionMode()).thenReturn( + TelephonyManager.NETWORK_SELECTION_MODE_AUTO); + when(mTestServiceState.getIsManualSelection()).thenReturn(true); + mController.init(mLifecycle, SUB_ID); + sleepAfterInit(); + + mController.updateUiAutoSelectValue(mTestServiceState); + + assertThat(mController.isChecked()).isFalse(); + assertThat(mSwitchPreference.isChecked()).isFalse(); + } + + @Test + public void updateUiAutoSelectValue_serviceStateGetIsAutoSelection_isCheckedTrue() { + when(mTelephonyManager.getNetworkSelectionMode()).thenReturn( + TelephonyManager.NETWORK_SELECTION_MODE_MANUAL); + when(mTestServiceState.getIsManualSelection()).thenReturn(false); + mController.init(mLifecycle, SUB_ID); + sleepAfterInit(); + + mController.updateUiAutoSelectValue(mTestServiceState); + + assertThat(mController.isChecked()).isTrue(); + assertThat(mSwitchPreference.isChecked()).isTrue(); } }