From 39d16d7bfb45cc430df8f74636371bb848589d23 Mon Sep 17 00:00:00 2001 From: hoffc Date: Tue, 24 Dec 2024 12:52:31 +0800 Subject: [PATCH] Fix sim status details not updated after sim hotswap Telephony callback may be removed in onPause during sim hotswap, but they are not re-registered in onResume for sub info still null. Listen sub info change and re-register telephony callback when need. Test: function test pass and SimStatusDialogControllerTest unit test pass. Change-Id: I17e60c9e3441fc593107048494f830408c37ae61 Bug: 384643359 --- .../simstatus/SimStatusDialogController.java | 87 +++++++++++-------- .../SimStatusDialogControllerTest.java | 60 ++++++++++++- 2 files changed, 111 insertions(+), 36 deletions(-) diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java index b5ee1d88108..89f286c368a 100644 --- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java +++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java @@ -100,11 +100,17 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { @VisibleForTesting static final int MAX_PHONE_COUNT_SINGLE_SIM = 1; - private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = + @VisibleForTesting + final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = new OnSubscriptionsChangedListener() { @Override public void onSubscriptionsChanged() { + SubscriptionInfo oldSubInfo = mSubscriptionInfo; mSubscriptionInfo = getPhoneSubscriptionInfo(mSlotIndex); + if (getSubId(oldSubInfo) != getSubId(mSubscriptionInfo)) { + unregisterTelephonyCallback(oldSubInfo); + registerTelephonyCallback(mSubscriptionInfo); + } updateSubscriptionStatus(); } }; @@ -123,7 +129,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { private final Context mContext; private boolean mShowLatestAreaInfo; - private boolean mIsRegisteredListener = false; private final BroadcastReceiver mAreaInfoReceiver = new BroadcastReceiver() { @Override @@ -137,7 +142,8 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { }; @VisibleForTesting - protected SimStatusDialogTelephonyCallback mTelephonyCallback; + protected final SimStatusDialogTelephonyCallback mTelephonyCallback = + new SimStatusDialogTelephonyCallback(); private CellBroadcastServiceConnection mCellBroadcastServiceConnection; @@ -181,8 +187,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { mContext = dialog.getContext(); mSlotIndex = slotId; mSubscriptionInfo = getPhoneSubscriptionInfo(slotId); - - mTelephonyManager = mContext.getSystemService(TelephonyManager.class); mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); mEuiccManager = mContext.getSystemService(EuiccManager.class); mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); @@ -201,11 +205,13 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { public void initialize() { if (mSubscriptionInfo == null) { - return; + // Should not depend on default sub TelephonyManager, but lots of code already uses it + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + } else { + mTelephonyManager = mContext.getSystemService(TelephonyManager.class) + .createForSubscriptionId(mSubscriptionInfo.getSubscriptionId()); } - mTelephonyManager = - getTelephonyManager().createForSubscriptionId(mSubscriptionInfo.getSubscriptionId()); - mTelephonyCallback = new SimStatusDialogTelephonyCallback(); + updateLatestAreaInfo(); updateSubscriptionStatus(); } @@ -222,6 +228,10 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { updateNetworkType(); updateRoamingStatus(serviceState); updateIccidNumber(); + + if (mSubscriptionInfo == null) { + updateDataState(TelephonyManager.DATA_UNKNOWN); + } } /** @@ -242,13 +252,9 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { */ @Override public void onResume(@NonNull LifecycleOwner owner) { - if (mSubscriptionInfo == null) { - return; - } - mTelephonyManager = getTelephonyManager().createForSubscriptionId( - mSubscriptionInfo.getSubscriptionId()); - getTelephonyManager() - .registerTelephonyCallback(mContext.getMainExecutor(), mTelephonyCallback); + // get the latest sub info for it may be updated in onPause/onStop status. + mSubscriptionInfo = getPhoneSubscriptionInfo(mSlotIndex); + registerTelephonyCallback(mSubscriptionInfo); mSubscriptionManager.addOnSubscriptionsChangedListener( mContext.getMainExecutor(), mOnSubscriptionsChangedListener); collectSimStatusDialogInfo(owner); @@ -259,8 +265,6 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { new IntentFilter(CellBroadcastIntents.ACTION_AREA_INFO_UPDATED), Context.RECEIVER_EXPORTED/*UNAUDITED*/); } - - mIsRegisteredListener = true; } /** @@ -268,22 +272,9 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { */ @Override public void onPause(@NonNull LifecycleOwner owner) { - if (mSubscriptionInfo == null) { - if (mIsRegisteredListener) { - mSubscriptionManager.removeOnSubscriptionsChangedListener( - mOnSubscriptionsChangedListener); - getTelephonyManager().unregisterTelephonyCallback(mTelephonyCallback); - if (mShowLatestAreaInfo) { - mContext.unregisterReceiver(mAreaInfoReceiver); - } - mIsRegisteredListener = false; - } - return; - } - - mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); - getTelephonyManager().unregisterTelephonyCallback(mTelephonyCallback); - + mSubscriptionManager.removeOnSubscriptionsChangedListener( + mOnSubscriptionsChangedListener); + unregisterTelephonyCallback(mSubscriptionInfo); if (mShowLatestAreaInfo) { mContext.unregisterReceiver(mAreaInfoReceiver); } @@ -555,6 +546,34 @@ public class SimStatusDialogController implements DefaultLifecycleObserver { return SubscriptionManager.from(mContext).getActiveSubscriptionInfoForSimSlotIndex(slotId); } + private void registerTelephonyCallback(SubscriptionInfo subInfo) { + if (subInfo == null) { + return; + } + + // No need to have a member to hold mTelephonyManager, leaving it as lots of code + // depending on it + mTelephonyManager = mContext.getSystemService(TelephonyManager.class) + .createForSubscriptionId(subInfo.getSubscriptionId()); + mTelephonyManager.registerTelephonyCallback( + mContext.getMainExecutor(), mTelephonyCallback); + } + + private void unregisterTelephonyCallback(SubscriptionInfo subInfo) { + if (subInfo == null) { + return; + } + + mContext.getSystemService(TelephonyManager.class) + .createForSubscriptionId(subInfo.getSubscriptionId()) + .unregisterTelephonyCallback(mTelephonyCallback); + } + + private int getSubId(SubscriptionInfo subInfo) { + return subInfo == null ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : + subInfo.getSubscriptionId(); + } + @VisibleForTesting class SimStatusDialogTelephonyCallback extends TelephonyCallback implements TelephonyCallback.DataConnectionStateListener, diff --git a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java index 3fa380828b9..d68ffa85717 100644 --- a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java +++ b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java @@ -17,6 +17,7 @@ package com.android.settings.deviceinfo.simstatus; import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.CELL_DATA_NETWORK_TYPE_VALUE_ID; +import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.CELLULAR_NETWORK_STATE; import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.CELL_VOICE_NETWORK_TYPE_VALUE_ID; import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.ICCID_INFO_LABEL_ID; import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.ICCID_INFO_VALUE_ID; @@ -30,8 +31,10 @@ import static com.android.settings.deviceinfo.simstatus.SimStatusDialogControlle import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController.SERVICE_STATE_VALUE_ID; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,6 +45,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.telephony.UiccCardInfo; import android.telephony.euicc.EuiccManager; @@ -65,6 +69,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Executor; @RunWith(AndroidJUnit4.class) public class SimStatusDialogControllerTest { @@ -74,6 +79,10 @@ public class SimStatusDialogControllerTest { @Mock private TelephonyManager mTelephonyManager; @Mock + private TelephonyManager mTelephonyManagerForSub1; + @Mock + private TelephonyManager mTelephonyManagerForSub2; + @Mock private SubscriptionInfo mSubscriptionInfo; @Mock private ServiceState mServiceState; @@ -94,6 +103,9 @@ public class SimStatusDialogControllerTest { private static final int MAX_PHONE_COUNT_DUAL_SIM = 2; + private static final int TEST_SUB_ID_1 = 1; + private static final int TEST_SUB_ID_2 = 2; + @Before @UiThreadTest public void setup() { @@ -106,6 +118,7 @@ public class SimStatusDialogControllerTest { mSubscriptionManager = spy(mContext.getSystemService(SubscriptionManager.class)); doReturn(mSubscriptionInfo).when(mSubscriptionManager) .getActiveSubscriptionInfoForSimSlotIndex(anyInt()); + doReturn(TEST_SUB_ID_1).when(mSubscriptionInfo).getSubscriptionId(); when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn( @@ -113,8 +126,10 @@ public class SimStatusDialogControllerTest { when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager); when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); - doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId( - anyInt()); + doReturn(mTelephonyManagerForSub1).when(mTelephonyManager).createForSubscriptionId( + TEST_SUB_ID_1); + doReturn(mTelephonyManagerForSub2).when(mTelephonyManager).createForSubscriptionId( + TEST_SUB_ID_2); doReturn(2).when(mTelephonyManager).getCardIdForDefaultEuicc(); doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mTelephonyManager).getDataNetworkType(); @@ -340,4 +355,45 @@ public class SimStatusDialogControllerTest { verify(mDialog).setSettingVisibility(IMS_REGISTRATION_STATE_LABEL_ID, false); verify(mDialog).setSettingVisibility(IMS_REGISTRATION_STATE_VALUE_ID, false); } + + @Test + public void onSubscriptionsChanged_updateSubInfoToNewSub_testTelephonyCallbackUnregRereg() { + // sub id changed from 1 to 2 + SubscriptionInfo subInfo2 = mock(SubscriptionInfo.class); + doReturn(TEST_SUB_ID_2).when(subInfo2).getSubscriptionId(); + doReturn(subInfo2).when(mSubscriptionManager) + .getActiveSubscriptionInfoForSimSlotIndex(anyInt()); + mController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + verify(mTelephonyManagerForSub2).registerTelephonyCallback(any(Executor.class), + any(TelephonyCallback.class)); + + // sub id changed from 2 to 1 + SubscriptionInfo subInfo1 = mock(SubscriptionInfo.class); + doReturn(TEST_SUB_ID_1).when(subInfo1).getSubscriptionId(); + doReturn(subInfo1).when(mSubscriptionManager) + .getActiveSubscriptionInfoForSimSlotIndex(anyInt()); + mController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + verify(mTelephonyManagerForSub2).unregisterTelephonyCallback( + mController.mTelephonyCallback); + verify(mTelephonyManagerForSub1).registerTelephonyCallback(any(Executor.class), + any(TelephonyCallback.class)); + } + + @Test + public void onSubscriptionsChanged_updateSubInfoToNull_testTelephonyCallbackUnreg() { + doReturn(null).when(mSubscriptionManager).getActiveSubscriptionInfoForSimSlotIndex( + anyInt()); + mController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + verify(mTelephonyManagerForSub1).unregisterTelephonyCallback( + mController.mTelephonyCallback); + } + + @Test + public void onSubscriptionsChanged_updateSubInfoToNull_shouldUpdateDataStatusToUnknown() { + doReturn(null).when(mSubscriptionManager).getActiveSubscriptionInfoForSimSlotIndex( + anyInt()); + mController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + final String unknownText = ResourcesUtils.getResourcesString(mContext, "radioInfo_unknown"); + verify(mDialog).setText(CELLULAR_NETWORK_STATE, unknownText); + } }