From 88717f2c3fc6cda7d235ad637893a0ec4ce9cc31 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 Merged-In: I81a597f28cf7ce25ff4eff5100bdb4d29c897a14 Merged-In: Idfc7a07106d552c35a94414bb14eac0fbdc3974f Change-Id: Ifb548de301021f992ef13c3d299de1642f379fbf --- .../telephony/MobileNetworkSettings.java | 2 +- .../gsm/AutoSelectPreferenceController.java | 95 ++++++++++++++++--- .../AutoSelectPreferenceControllerTest.java | 53 ++++++++++- 3 files changed, 133 insertions(+), 17 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index 05e58e96ed7..98add25fdaa 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -211,7 +211,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { 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 a19702fcb06..d6da17d00b9 100644 --- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java +++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.java @@ -31,9 +31,12 @@ import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; 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; @@ -60,6 +63,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; @@ -74,6 +80,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon private AtomicBoolean mUpdatingConfig; private int mCacheOfModeStatus; private AtomicLong mRecursiveUpdate; + TelephonyCallbackListener mTelephonyCallbackListener; public AutoSelectPreferenceController(Context context, String key) { super(context, key); @@ -88,6 +95,7 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon new HandlerExecutor(mUiHandler)); mAllowedNetworkTypesListener.setAllowedNetworkTypesListener( () -> updatePreference()); + mTelephonyCallbackListener = new TelephonyCallbackListener(); } private void updatePreference() { @@ -104,11 +112,14 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon @OnLifecycleEvent(ON_START) public void onStart() { mAllowedNetworkTypesListener.register(mContext, mSubId); + mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mUiHandler), + mTelephonyCallbackListener); } @OnLifecycleEvent(ON_STOP) public void onStop() { mAllowedNetworkTypesListener.unregister(mContext, mSubId); + mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallbackListener); } @Override @@ -127,12 +138,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; } @@ -195,12 +200,23 @@ 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(); + updateListenerValue(); + dismissProgressBar(); + }); + }); }, Math.max(MINIMUM_DIALOG_TIME_MILLIS - durationMillis, 0)); }); } @@ -208,7 +224,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); @@ -219,6 +235,19 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL) : false; + ThreadUtils.postOnBackgroundThread(() -> { + queryNetworkSelectionMode(INTERNAL_LOG_TAG_INIT); + + //Update UI in UI thread + mUiHandler.post(() -> { + if (mSwitchPreference != null) { + mRecursiveUpdate.getAndIncrement(); + mSwitchPreference.setChecked(isChecked()); + mRecursiveUpdate.decrementAndGet(); + updateListenerValue(); + } + }); + }); return this; } @@ -228,6 +257,39 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon return this; } + private void queryNetworkSelectionMode(String tag) { + mCacheOfModeStatus = mTelephonyManager.getNetworkSelectionMode(); + Log.d(LOG_TAG, tag + ": query command done. mCacheOfModeStatus: " + mCacheOfModeStatus); + } + + @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); + + mRecursiveUpdate.getAndIncrement(); + updateState(mSwitchPreference); + mRecursiveUpdate.decrementAndGet(); + updateListenerValue(); + } + } + + private void updateListenerValue() { + for (OnNetworkSelectModeListener lsn : mListeners) { + lsn.onNetworkSelectModeUpdated(mCacheOfModeStatus); + } + } + private void showAutoSelectProgressBar() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(mContext); @@ -258,4 +320,13 @@ public class AutoSelectPreferenceController extends TelephonyTogglePreferenceCon public interface OnNetworkSelectModeListener { void onNetworkSelectModeUpdated(int mode); } + + private class TelephonyCallbackListener extends TelephonyCallback + implements TelephonyCallback.ServiceStateListener { + + @Override + public void onServiceStateChanged(ServiceState serviceState) { + updateUiAutoSelectValue(serviceState); + } + } } 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(); } }