Loading src/com/android/settings/network/ActiveSubsciptionsListener.java +60 −33 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; Loading @@ -31,6 +33,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.TelephonyIntents; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * A listener for active subscription change Loading @@ -43,18 +46,26 @@ public abstract class ActiveSubsciptionsListener /** * Constructor * * @param context of this listener * @param looper {@code Looper} of this listener * @param context {@code Context} of this listener */ public ActiveSubsciptionsListener(Context context) { public ActiveSubsciptionsListener(Looper looper, Context context) { mLooper = looper; mContext = context; mCacheState = new AtomicInteger(STATE_NOT_LISTENING); mMaxActiveSubscriptionInfos = new AtomicInteger(MAX_SUBSCRIPTION_UNKNOWN); mSubscriptionChangeIntentFilter = new IntentFilter(); mSubscriptionChangeIntentFilter.addAction( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); mSubscriptionChangeIntentFilter.addAction( TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); } mSubscriptionChangeReceiver = new BroadcastReceiver() { @VisibleForTesting BroadcastReceiver getSubscriptionChangeReceiver() { return new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (isInitialStickyBroadcast()) { Loading @@ -77,18 +88,24 @@ public abstract class ActiveSubsciptionsListener }; } private Looper mLooper; private Context mContext; private boolean mIsMonitoringDataChange; private boolean mIsCachedDataAvailable; private static final int STATE_NOT_LISTENING = 0; private static final int STATE_STOPPING = 1; private static final int STATE_PREPARING = 2; private static final int STATE_LISTENING = 3; private static final int STATE_DATA_CACHED = 4; private AtomicInteger mCacheState; private SubscriptionManager mSubscriptionManager; private IntentFilter mSubscriptionChangeIntentFilter; private BroadcastReceiver mSubscriptionChangeReceiver; @VisibleForTesting BroadcastReceiver mSubscriptionChangeReceiver; private static final int MAX_SUBSCRIPTION_UNKNOWN = -1; private Integer mMaxActiveSubscriptionInfos; private AtomicInteger mMaxActiveSubscriptionInfos; private List<SubscriptionInfo> mCachedActiveSubscriptionInfo; /** Loading Loading @@ -135,16 +152,14 @@ public abstract class ActiveSubsciptionsListener * @return max. number of active subscription info(s) */ public int getActiveSubscriptionInfoCountMax() { int count = 0; if (mMaxActiveSubscriptionInfos == null) { count = getSubscriptionManager().getActiveSubscriptionInfoCountMax(); if (mIsMonitoringDataChange) { mMaxActiveSubscriptionInfos = count; int cacheState = mCacheState.get(); if (cacheState < STATE_LISTENING) { return getSubscriptionManager().getActiveSubscriptionInfoCountMax(); } } else { count = mMaxActiveSubscriptionInfos.intValue(); } return count; mMaxActiveSubscriptionInfos.compareAndSet(MAX_SUBSCRIPTION_UNKNOWN, getSubscriptionManager().getActiveSubscriptionInfoCountMax()); return mMaxActiveSubscriptionInfos.get(); } /** Loading @@ -153,11 +168,11 @@ public abstract class ActiveSubsciptionsListener * @return A list of active subscription info */ public List<SubscriptionInfo> getActiveSubscriptionsInfo() { if (mIsCachedDataAvailable) { if (mCacheState.get() >= STATE_DATA_CACHED) { return mCachedActiveSubscriptionInfo; } mIsCachedDataAvailable = mIsMonitoringDataChange; mCachedActiveSubscriptionInfo = getSubscriptionManager().getActiveSubscriptionInfoList(); mCacheState.compareAndSet(STATE_LISTENING, STATE_DATA_CACHED); if ((mCachedActiveSubscriptionInfo == null) || (mCachedActiveSubscriptionInfo.size() <= 0)) { Loading Loading @@ -208,7 +223,7 @@ public abstract class ActiveSubsciptionsListener * @return A subscription info which is accessible list */ public SubscriptionInfo getAccessibleSubscriptionInfo(int subId) { if (mIsCachedDataAvailable) { if (mCacheState.get() >= STATE_DATA_CACHED) { final SubscriptionInfo activeSubInfo = getActiveSubscriptionInfo(subId); if (activeSubInfo != null) { return activeSubInfo; Loading @@ -231,37 +246,49 @@ public abstract class ActiveSubsciptionsListener * Clear data cached within listener */ public void clearCache() { mIsCachedDataAvailable = false; mMaxActiveSubscriptionInfos = null; mMaxActiveSubscriptionInfos.set(MAX_SUBSCRIPTION_UNKNOWN); mCacheState.compareAndSet(STATE_DATA_CACHED, STATE_LISTENING); mCachedActiveSubscriptionInfo = null; } private void monitorSubscriptionsChange(boolean on) { if (mIsMonitoringDataChange == on) { if (on) { if (!mCacheState.compareAndSet(STATE_NOT_LISTENING, STATE_PREPARING)) { return; } mIsMonitoringDataChange = on; if (on) { if (mSubscriptionChangeReceiver == null) { mSubscriptionChangeReceiver = getSubscriptionChangeReceiver(); } mContext.registerReceiver(mSubscriptionChangeReceiver, mSubscriptionChangeIntentFilter); mSubscriptionChangeIntentFilter, null, new Handler(mLooper)); getSubscriptionManager().addOnSubscriptionsChangedListener(this); listenerNotify(); } else { mCacheState.compareAndSet(STATE_PREPARING, STATE_LISTENING); return; } final int currentState = mCacheState.getAndSet(STATE_STOPPING); if (currentState <= STATE_STOPPING) { mCacheState.compareAndSet(STATE_STOPPING, currentState); return; } if (mSubscriptionChangeReceiver != null) { mContext.unregisterReceiver(mSubscriptionChangeReceiver); } getSubscriptionManager().removeOnSubscriptionsChangedListener(this); clearCache(); } mCacheState.compareAndSet(STATE_STOPPING, STATE_NOT_LISTENING); } private void listenerNotify() { if (!mIsMonitoringDataChange) { if (mCacheState.get() < STATE_LISTENING) { return; } onChanged(); } private boolean clearCachedSubId(int subId) { if (!mIsCachedDataAvailable) { if (mCacheState.get() < STATE_DATA_CACHED) { return false; } if (mCachedActiveSubscriptionInfo == null) { Loading src/com/android/settings/network/GlobalSettingsChangeListener.java +24 −11 Original line number Diff line number Diff line Loading @@ -24,12 +24,15 @@ import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.provider.Settings; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import java.util.concurrent.atomic.AtomicBoolean; /** * A listener for Settings.Global configuration change, with support of Lifecycle */ Loading @@ -39,19 +42,33 @@ public abstract class GlobalSettingsChangeListener extends ContentObserver /** * Constructor * * @param context of this listener * @param context {@code Context} of this listener * @param field field of Global Settings */ public GlobalSettingsChangeListener(Context context, String field) { super(new Handler()); this(Looper.getMainLooper(), context, field); } /** * Constructor * * @param looper {@code Looper} for processing callback * @param context {@code Context} of this listener * @param field field of Global Settings */ public GlobalSettingsChangeListener(Looper looper, Context context, String field) { super(new Handler(looper)); mContext = context; mField = field; mUri = Settings.Global.getUriFor(field); mListening = new AtomicBoolean(false); monitorUri(true); } private Context mContext; private String mField; private Uri mUri; private AtomicBoolean mListening; private Lifecycle mLifecycle; /** Loading @@ -75,7 +92,7 @@ public abstract class GlobalSettingsChangeListener extends ContentObserver } public void onChange(boolean selfChange) { if (!isMonitoring()) { if (!mListening.get()) { return; } onChanged(mField); Loading Loading @@ -104,20 +121,16 @@ public abstract class GlobalSettingsChangeListener extends ContentObserver notifyChangeBasedOn(null); } private boolean isMonitoring() { return (mUri != null); } private void monitorUri(boolean on) { if (isMonitoring() == on) { if (!mListening.compareAndSet(!on, on)) { return; } if (mUri == null) { mUri = Settings.Global.getUriFor(mField); if (on) { mContext.getContentResolver().registerContentObserver(mUri, false, this); return; } mUri = null; mContext.getContentResolver().unregisterContentObserver(this); } } src/com/android/settings/network/ProxySubscriptionManager.java +6 −6 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static androidx.lifecycle.Lifecycle.Event.ON_START; import static androidx.lifecycle.Lifecycle.Event.ON_STOP; import android.content.Context; import android.os.Looper; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; Loading Loading @@ -71,18 +72,18 @@ public class ProxySubscriptionManager implements LifecycleObserver { private static ProxySubscriptionManager sSingleton; private ProxySubscriptionManager(Context context) { mContext = context; final Looper looper = Looper.getMainLooper(); mActiveSubscriptionsListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mSubsciptionsMonitor = new ActiveSubsciptionsListener(context) { mSubsciptionsMonitor = new ActiveSubsciptionsListener(looper, context) { public void onChanged() { notifyAllListeners(); } }; mAirplaneModeMonitor = new GlobalSettingsChangeListener(context, Settings.Global.AIRPLANE_MODE_ON) { mAirplaneModeMonitor = new GlobalSettingsChangeListener(looper, context, Settings.Global.AIRPLANE_MODE_ON) { public void onChanged(String field) { mSubsciptionsMonitor.clearCache(); notifyAllListeners(); Loading @@ -93,7 +94,6 @@ public class ProxySubscriptionManager implements LifecycleObserver { } private Lifecycle mLifecycle; private Context mContext; private ActiveSubsciptionsListener mSubsciptionsMonitor; private GlobalSettingsChangeListener mAirplaneModeMonitor; Loading Loading @@ -140,7 +140,7 @@ public class ProxySubscriptionManager implements LifecycleObserver { @OnLifecycleEvent(ON_DESTROY) void onDestroy() { mSubsciptionsMonitor.stop(); mAirplaneModeMonitor.close(); if (mLifecycle != null) { mLifecycle.removeObserver(this); Loading tests/robotests/src/com/android/settings/network/ActiveSubsciptionsListenerTest.java +98 −53 Original line number Diff line number Diff line Loading @@ -16,111 +16,157 @@ package com.android.settings.network; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Looper; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.telephony.TelephonyIntents; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowBroadcastReceiver; import org.robolectric.shadows.ShadowContextImpl; import org.robolectric.shadows.ShadowSubscriptionManager; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @RunWith(RobolectricTestRunner.class) @RunWith(AndroidJUnit4.class) public class ActiveSubsciptionsListenerTest { private static final int SUB_ID1 = 3; private static final int SUB_ID2 = 7; @Mock private SubscriptionManager mSubscriptionManager; @Mock private SubscriptionInfo mSubscriptionInfo1; @Mock private SubscriptionInfo mSubscriptionInfo2; private static final Intent INTENT_RADIO_TECHNOLOGY_CHANGED = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); private static final Intent INTENT_MULTI_SIM_CONFIG_CHANGED = new Intent(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED); private static final Intent INTENT_CARRIER_CONFIG_CHANGED = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); private Context mContext; private ActiveSubsciptionsListener mListener; private ShadowContextImpl mShadowContextImpl; private SubscriptionManager mSubscriptionManager; private ShadowSubscriptionManager mShadowSubscriptionManager; private List<SubscriptionInfo> mActiveSubscriptions; private BroadcastReceiver mSubscriptionChangeReceiver; private ActiveSubsciptionsListenerImpl mListener; private BroadcastReceiver mReceiver; private ShadowBroadcastReceiver mShadowReceiver; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); mContext = RuntimeEnvironment.application.getBaseContext(); mShadowContextImpl = Shadow.extract(mContext); mSubscriptionManager = spy(mContext.getSystemService(SubscriptionManager.class)); mShadowSubscriptionManager = shadowOf(mSubscriptionManager); mActiveSubscriptions = new ArrayList<SubscriptionInfo>(); mActiveSubscriptions.add(ShadowSubscriptionManager.SubscriptionInfoBuilder .newBuilder().setId(SUB_ID1).buildSubscriptionInfo()); mActiveSubscriptions.add(ShadowSubscriptionManager.SubscriptionInfoBuilder .newBuilder().setId(SUB_ID2).buildSubscriptionInfo()); mShadowSubscriptionManager.setActiveSubscriptionInfoList(mActiveSubscriptions); mListener = spy(new ActiveSubsciptionsListenerImpl(Looper.getMainLooper(), mContext)); doReturn(mSubscriptionManager).when(mListener).getSubscriptionManager(); mReceiver = mListener.getSubscriptionChangeReceiver(); mShadowReceiver = shadowOf(mReceiver); doReturn(mReceiver).when(mListener).getSubscriptionChangeReceiver(); } @After public void cleanUp() { mListener.stop(); } private class ActiveSubsciptionsListenerImpl extends ActiveSubsciptionsListener { private ActiveSubsciptionsListenerImpl(Looper looper, Context context) { super(looper, context); } public void onChanged() {} } private void sendIntentToReceiver(Intent intent) { mShadowReceiver.onReceive(mContext, intent, new AtomicBoolean(false)); } @Test public void constructor_noListeningWasSetup() { mListener = spy(new ActiveSubsciptionsListener(mContext) { public void onChanged() {} }); verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(any()); verify(mContext, never()).registerReceiver(any(), any()); verify(mListener, never()).onChanged(); } @Test public void start_onChangedShouldAlwaysBeCalled() { mListener = spy(new ActiveSubsciptionsListener(mContext) { public void onChanged() {} }); mSubscriptionChangeReceiver = spy(mListener.mSubscriptionChangeReceiver); when(mSubscriptionChangeReceiver.isInitialStickyBroadcast()).thenReturn(false); public void start_configChangedIntent_onChangedShouldBeCalled() { sendIntentToReceiver(INTENT_RADIO_TECHNOLOGY_CHANGED); sendIntentToReceiver(INTENT_MULTI_SIM_CONFIG_CHANGED); verify(mListener, never()).onChanged(); mListener.start(); mActiveSubscriptions.add(mSubscriptionInfo1); mActiveSubscriptions.add(mSubscriptionInfo2); when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(mActiveSubscriptions); sendIntentToReceiver(INTENT_RADIO_TECHNOLOGY_CHANGED); verify(mListener, times(1)).onChanged(); final Intent intentSubscription = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); final Intent intentRadioTech = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); sendIntentToReceiver(INTENT_MULTI_SIM_CONFIG_CHANGED); verify(mListener, times(2)).onChanged(); mSubscriptionChangeReceiver.onReceive(mContext, intentSubscription); mSubscriptionChangeReceiver.onReceive(mContext, intentRadioTech); mListener.stop(); sendIntentToReceiver(INTENT_RADIO_TECHNOLOGY_CHANGED); sendIntentToReceiver(INTENT_MULTI_SIM_CONFIG_CHANGED); verify(mListener, times(2)).onChanged(); } @Test public void start_carrierConfigChangedIntent_onChangedWhenSubIdBeenCached() { sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, never()).onChanged(); mListener.start(); mSubscriptionChangeReceiver.onReceive(mContext, intentSubscription); verify(mListener, atLeastOnce()).onChanged(); mListener.getActiveSubscriptionsInfo(); mSubscriptionChangeReceiver.onReceive(mContext, intentRadioTech); sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, never()).onChanged(); INTENT_CARRIER_CONFIG_CHANGED.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID2); sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, times(1)).onChanged(); mListener.stop(); mContext.sendStickyBroadcast(intentSubscription); mContext.sendStickyBroadcast(intentRadioTech); sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, times(1)).onChanged(); } @Test public void start_alwaysFetchAndCacheResult() { mListener = spy(new ActiveSubsciptionsListener(mContext) { public void onChanged() {} }); mActiveSubscriptions.add(mSubscriptionInfo1); mActiveSubscriptions.add(mSubscriptionInfo2); mListener.start(); List<SubscriptionInfo> subInfoList = null; Loading @@ -130,8 +176,7 @@ public class ActiveSubsciptionsListenerTest { if (mActiveSubscriptions.size() > numberOfSubInfo) { mActiveSubscriptions.remove(numberOfSubInfo); } when(mSubscriptionManager.getActiveSubscriptionInfoList()) .thenReturn(mActiveSubscriptions); mShadowSubscriptionManager.setActiveSubscriptionInfoList(mActiveSubscriptions); // fetch twice and test if they generated access to SubscriptionManager only once subInfoList = mListener.getActiveSubscriptionsInfo(); Loading @@ -143,7 +188,7 @@ public class ActiveSubsciptionsListenerTest { mListener.clearCache(); } when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(null); mShadowSubscriptionManager.setActiveSubscriptionInfoList(null); // fetch twice and test if they generated access to SubscriptionManager only once subInfoList = mListener.getActiveSubscriptionsInfo(); Loading tests/robotests/src/com/android/settings/network/GlobalSettingsChangeListenerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Looper; import android.provider.Settings; import androidx.lifecycle.Lifecycle; Loading Loading @@ -49,7 +50,8 @@ public class GlobalSettingsChangeListenerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mListener = spy(new GlobalSettingsChangeListener(mContext, SETTINGS_FIELD) { mListener = spy(new GlobalSettingsChangeListener(Looper.getMainLooper(), mContext, SETTINGS_FIELD) { public void onChanged(String field) {} }); Loading Loading
src/com/android/settings/network/ActiveSubsciptionsListener.java +60 −33 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; Loading @@ -31,6 +33,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.TelephonyIntents; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * A listener for active subscription change Loading @@ -43,18 +46,26 @@ public abstract class ActiveSubsciptionsListener /** * Constructor * * @param context of this listener * @param looper {@code Looper} of this listener * @param context {@code Context} of this listener */ public ActiveSubsciptionsListener(Context context) { public ActiveSubsciptionsListener(Looper looper, Context context) { mLooper = looper; mContext = context; mCacheState = new AtomicInteger(STATE_NOT_LISTENING); mMaxActiveSubscriptionInfos = new AtomicInteger(MAX_SUBSCRIPTION_UNKNOWN); mSubscriptionChangeIntentFilter = new IntentFilter(); mSubscriptionChangeIntentFilter.addAction( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); mSubscriptionChangeIntentFilter.addAction( TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); } mSubscriptionChangeReceiver = new BroadcastReceiver() { @VisibleForTesting BroadcastReceiver getSubscriptionChangeReceiver() { return new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (isInitialStickyBroadcast()) { Loading @@ -77,18 +88,24 @@ public abstract class ActiveSubsciptionsListener }; } private Looper mLooper; private Context mContext; private boolean mIsMonitoringDataChange; private boolean mIsCachedDataAvailable; private static final int STATE_NOT_LISTENING = 0; private static final int STATE_STOPPING = 1; private static final int STATE_PREPARING = 2; private static final int STATE_LISTENING = 3; private static final int STATE_DATA_CACHED = 4; private AtomicInteger mCacheState; private SubscriptionManager mSubscriptionManager; private IntentFilter mSubscriptionChangeIntentFilter; private BroadcastReceiver mSubscriptionChangeReceiver; @VisibleForTesting BroadcastReceiver mSubscriptionChangeReceiver; private static final int MAX_SUBSCRIPTION_UNKNOWN = -1; private Integer mMaxActiveSubscriptionInfos; private AtomicInteger mMaxActiveSubscriptionInfos; private List<SubscriptionInfo> mCachedActiveSubscriptionInfo; /** Loading Loading @@ -135,16 +152,14 @@ public abstract class ActiveSubsciptionsListener * @return max. number of active subscription info(s) */ public int getActiveSubscriptionInfoCountMax() { int count = 0; if (mMaxActiveSubscriptionInfos == null) { count = getSubscriptionManager().getActiveSubscriptionInfoCountMax(); if (mIsMonitoringDataChange) { mMaxActiveSubscriptionInfos = count; int cacheState = mCacheState.get(); if (cacheState < STATE_LISTENING) { return getSubscriptionManager().getActiveSubscriptionInfoCountMax(); } } else { count = mMaxActiveSubscriptionInfos.intValue(); } return count; mMaxActiveSubscriptionInfos.compareAndSet(MAX_SUBSCRIPTION_UNKNOWN, getSubscriptionManager().getActiveSubscriptionInfoCountMax()); return mMaxActiveSubscriptionInfos.get(); } /** Loading @@ -153,11 +168,11 @@ public abstract class ActiveSubsciptionsListener * @return A list of active subscription info */ public List<SubscriptionInfo> getActiveSubscriptionsInfo() { if (mIsCachedDataAvailable) { if (mCacheState.get() >= STATE_DATA_CACHED) { return mCachedActiveSubscriptionInfo; } mIsCachedDataAvailable = mIsMonitoringDataChange; mCachedActiveSubscriptionInfo = getSubscriptionManager().getActiveSubscriptionInfoList(); mCacheState.compareAndSet(STATE_LISTENING, STATE_DATA_CACHED); if ((mCachedActiveSubscriptionInfo == null) || (mCachedActiveSubscriptionInfo.size() <= 0)) { Loading Loading @@ -208,7 +223,7 @@ public abstract class ActiveSubsciptionsListener * @return A subscription info which is accessible list */ public SubscriptionInfo getAccessibleSubscriptionInfo(int subId) { if (mIsCachedDataAvailable) { if (mCacheState.get() >= STATE_DATA_CACHED) { final SubscriptionInfo activeSubInfo = getActiveSubscriptionInfo(subId); if (activeSubInfo != null) { return activeSubInfo; Loading @@ -231,37 +246,49 @@ public abstract class ActiveSubsciptionsListener * Clear data cached within listener */ public void clearCache() { mIsCachedDataAvailable = false; mMaxActiveSubscriptionInfos = null; mMaxActiveSubscriptionInfos.set(MAX_SUBSCRIPTION_UNKNOWN); mCacheState.compareAndSet(STATE_DATA_CACHED, STATE_LISTENING); mCachedActiveSubscriptionInfo = null; } private void monitorSubscriptionsChange(boolean on) { if (mIsMonitoringDataChange == on) { if (on) { if (!mCacheState.compareAndSet(STATE_NOT_LISTENING, STATE_PREPARING)) { return; } mIsMonitoringDataChange = on; if (on) { if (mSubscriptionChangeReceiver == null) { mSubscriptionChangeReceiver = getSubscriptionChangeReceiver(); } mContext.registerReceiver(mSubscriptionChangeReceiver, mSubscriptionChangeIntentFilter); mSubscriptionChangeIntentFilter, null, new Handler(mLooper)); getSubscriptionManager().addOnSubscriptionsChangedListener(this); listenerNotify(); } else { mCacheState.compareAndSet(STATE_PREPARING, STATE_LISTENING); return; } final int currentState = mCacheState.getAndSet(STATE_STOPPING); if (currentState <= STATE_STOPPING) { mCacheState.compareAndSet(STATE_STOPPING, currentState); return; } if (mSubscriptionChangeReceiver != null) { mContext.unregisterReceiver(mSubscriptionChangeReceiver); } getSubscriptionManager().removeOnSubscriptionsChangedListener(this); clearCache(); } mCacheState.compareAndSet(STATE_STOPPING, STATE_NOT_LISTENING); } private void listenerNotify() { if (!mIsMonitoringDataChange) { if (mCacheState.get() < STATE_LISTENING) { return; } onChanged(); } private boolean clearCachedSubId(int subId) { if (!mIsCachedDataAvailable) { if (mCacheState.get() < STATE_DATA_CACHED) { return false; } if (mCachedActiveSubscriptionInfo == null) { Loading
src/com/android/settings/network/GlobalSettingsChangeListener.java +24 −11 Original line number Diff line number Diff line Loading @@ -24,12 +24,15 @@ import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.provider.Settings; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import java.util.concurrent.atomic.AtomicBoolean; /** * A listener for Settings.Global configuration change, with support of Lifecycle */ Loading @@ -39,19 +42,33 @@ public abstract class GlobalSettingsChangeListener extends ContentObserver /** * Constructor * * @param context of this listener * @param context {@code Context} of this listener * @param field field of Global Settings */ public GlobalSettingsChangeListener(Context context, String field) { super(new Handler()); this(Looper.getMainLooper(), context, field); } /** * Constructor * * @param looper {@code Looper} for processing callback * @param context {@code Context} of this listener * @param field field of Global Settings */ public GlobalSettingsChangeListener(Looper looper, Context context, String field) { super(new Handler(looper)); mContext = context; mField = field; mUri = Settings.Global.getUriFor(field); mListening = new AtomicBoolean(false); monitorUri(true); } private Context mContext; private String mField; private Uri mUri; private AtomicBoolean mListening; private Lifecycle mLifecycle; /** Loading @@ -75,7 +92,7 @@ public abstract class GlobalSettingsChangeListener extends ContentObserver } public void onChange(boolean selfChange) { if (!isMonitoring()) { if (!mListening.get()) { return; } onChanged(mField); Loading Loading @@ -104,20 +121,16 @@ public abstract class GlobalSettingsChangeListener extends ContentObserver notifyChangeBasedOn(null); } private boolean isMonitoring() { return (mUri != null); } private void monitorUri(boolean on) { if (isMonitoring() == on) { if (!mListening.compareAndSet(!on, on)) { return; } if (mUri == null) { mUri = Settings.Global.getUriFor(mField); if (on) { mContext.getContentResolver().registerContentObserver(mUri, false, this); return; } mUri = null; mContext.getContentResolver().unregisterContentObserver(this); } }
src/com/android/settings/network/ProxySubscriptionManager.java +6 −6 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static androidx.lifecycle.Lifecycle.Event.ON_START; import static androidx.lifecycle.Lifecycle.Event.ON_STOP; import android.content.Context; import android.os.Looper; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; Loading Loading @@ -71,18 +72,18 @@ public class ProxySubscriptionManager implements LifecycleObserver { private static ProxySubscriptionManager sSingleton; private ProxySubscriptionManager(Context context) { mContext = context; final Looper looper = Looper.getMainLooper(); mActiveSubscriptionsListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mSubsciptionsMonitor = new ActiveSubsciptionsListener(context) { mSubsciptionsMonitor = new ActiveSubsciptionsListener(looper, context) { public void onChanged() { notifyAllListeners(); } }; mAirplaneModeMonitor = new GlobalSettingsChangeListener(context, Settings.Global.AIRPLANE_MODE_ON) { mAirplaneModeMonitor = new GlobalSettingsChangeListener(looper, context, Settings.Global.AIRPLANE_MODE_ON) { public void onChanged(String field) { mSubsciptionsMonitor.clearCache(); notifyAllListeners(); Loading @@ -93,7 +94,6 @@ public class ProxySubscriptionManager implements LifecycleObserver { } private Lifecycle mLifecycle; private Context mContext; private ActiveSubsciptionsListener mSubsciptionsMonitor; private GlobalSettingsChangeListener mAirplaneModeMonitor; Loading Loading @@ -140,7 +140,7 @@ public class ProxySubscriptionManager implements LifecycleObserver { @OnLifecycleEvent(ON_DESTROY) void onDestroy() { mSubsciptionsMonitor.stop(); mAirplaneModeMonitor.close(); if (mLifecycle != null) { mLifecycle.removeObserver(this); Loading
tests/robotests/src/com/android/settings/network/ActiveSubsciptionsListenerTest.java +98 −53 Original line number Diff line number Diff line Loading @@ -16,111 +16,157 @@ package com.android.settings.network; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Looper; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.telephony.TelephonyIntents; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowBroadcastReceiver; import org.robolectric.shadows.ShadowContextImpl; import org.robolectric.shadows.ShadowSubscriptionManager; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @RunWith(RobolectricTestRunner.class) @RunWith(AndroidJUnit4.class) public class ActiveSubsciptionsListenerTest { private static final int SUB_ID1 = 3; private static final int SUB_ID2 = 7; @Mock private SubscriptionManager mSubscriptionManager; @Mock private SubscriptionInfo mSubscriptionInfo1; @Mock private SubscriptionInfo mSubscriptionInfo2; private static final Intent INTENT_RADIO_TECHNOLOGY_CHANGED = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); private static final Intent INTENT_MULTI_SIM_CONFIG_CHANGED = new Intent(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED); private static final Intent INTENT_CARRIER_CONFIG_CHANGED = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); private Context mContext; private ActiveSubsciptionsListener mListener; private ShadowContextImpl mShadowContextImpl; private SubscriptionManager mSubscriptionManager; private ShadowSubscriptionManager mShadowSubscriptionManager; private List<SubscriptionInfo> mActiveSubscriptions; private BroadcastReceiver mSubscriptionChangeReceiver; private ActiveSubsciptionsListenerImpl mListener; private BroadcastReceiver mReceiver; private ShadowBroadcastReceiver mShadowReceiver; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); mContext = RuntimeEnvironment.application.getBaseContext(); mShadowContextImpl = Shadow.extract(mContext); mSubscriptionManager = spy(mContext.getSystemService(SubscriptionManager.class)); mShadowSubscriptionManager = shadowOf(mSubscriptionManager); mActiveSubscriptions = new ArrayList<SubscriptionInfo>(); mActiveSubscriptions.add(ShadowSubscriptionManager.SubscriptionInfoBuilder .newBuilder().setId(SUB_ID1).buildSubscriptionInfo()); mActiveSubscriptions.add(ShadowSubscriptionManager.SubscriptionInfoBuilder .newBuilder().setId(SUB_ID2).buildSubscriptionInfo()); mShadowSubscriptionManager.setActiveSubscriptionInfoList(mActiveSubscriptions); mListener = spy(new ActiveSubsciptionsListenerImpl(Looper.getMainLooper(), mContext)); doReturn(mSubscriptionManager).when(mListener).getSubscriptionManager(); mReceiver = mListener.getSubscriptionChangeReceiver(); mShadowReceiver = shadowOf(mReceiver); doReturn(mReceiver).when(mListener).getSubscriptionChangeReceiver(); } @After public void cleanUp() { mListener.stop(); } private class ActiveSubsciptionsListenerImpl extends ActiveSubsciptionsListener { private ActiveSubsciptionsListenerImpl(Looper looper, Context context) { super(looper, context); } public void onChanged() {} } private void sendIntentToReceiver(Intent intent) { mShadowReceiver.onReceive(mContext, intent, new AtomicBoolean(false)); } @Test public void constructor_noListeningWasSetup() { mListener = spy(new ActiveSubsciptionsListener(mContext) { public void onChanged() {} }); verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(any()); verify(mContext, never()).registerReceiver(any(), any()); verify(mListener, never()).onChanged(); } @Test public void start_onChangedShouldAlwaysBeCalled() { mListener = spy(new ActiveSubsciptionsListener(mContext) { public void onChanged() {} }); mSubscriptionChangeReceiver = spy(mListener.mSubscriptionChangeReceiver); when(mSubscriptionChangeReceiver.isInitialStickyBroadcast()).thenReturn(false); public void start_configChangedIntent_onChangedShouldBeCalled() { sendIntentToReceiver(INTENT_RADIO_TECHNOLOGY_CHANGED); sendIntentToReceiver(INTENT_MULTI_SIM_CONFIG_CHANGED); verify(mListener, never()).onChanged(); mListener.start(); mActiveSubscriptions.add(mSubscriptionInfo1); mActiveSubscriptions.add(mSubscriptionInfo2); when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(mActiveSubscriptions); sendIntentToReceiver(INTENT_RADIO_TECHNOLOGY_CHANGED); verify(mListener, times(1)).onChanged(); final Intent intentSubscription = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); final Intent intentRadioTech = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); sendIntentToReceiver(INTENT_MULTI_SIM_CONFIG_CHANGED); verify(mListener, times(2)).onChanged(); mSubscriptionChangeReceiver.onReceive(mContext, intentSubscription); mSubscriptionChangeReceiver.onReceive(mContext, intentRadioTech); mListener.stop(); sendIntentToReceiver(INTENT_RADIO_TECHNOLOGY_CHANGED); sendIntentToReceiver(INTENT_MULTI_SIM_CONFIG_CHANGED); verify(mListener, times(2)).onChanged(); } @Test public void start_carrierConfigChangedIntent_onChangedWhenSubIdBeenCached() { sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, never()).onChanged(); mListener.start(); mSubscriptionChangeReceiver.onReceive(mContext, intentSubscription); verify(mListener, atLeastOnce()).onChanged(); mListener.getActiveSubscriptionsInfo(); mSubscriptionChangeReceiver.onReceive(mContext, intentRadioTech); sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, never()).onChanged(); INTENT_CARRIER_CONFIG_CHANGED.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID2); sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, times(1)).onChanged(); mListener.stop(); mContext.sendStickyBroadcast(intentSubscription); mContext.sendStickyBroadcast(intentRadioTech); sendIntentToReceiver(INTENT_CARRIER_CONFIG_CHANGED); verify(mListener, times(1)).onChanged(); } @Test public void start_alwaysFetchAndCacheResult() { mListener = spy(new ActiveSubsciptionsListener(mContext) { public void onChanged() {} }); mActiveSubscriptions.add(mSubscriptionInfo1); mActiveSubscriptions.add(mSubscriptionInfo2); mListener.start(); List<SubscriptionInfo> subInfoList = null; Loading @@ -130,8 +176,7 @@ public class ActiveSubsciptionsListenerTest { if (mActiveSubscriptions.size() > numberOfSubInfo) { mActiveSubscriptions.remove(numberOfSubInfo); } when(mSubscriptionManager.getActiveSubscriptionInfoList()) .thenReturn(mActiveSubscriptions); mShadowSubscriptionManager.setActiveSubscriptionInfoList(mActiveSubscriptions); // fetch twice and test if they generated access to SubscriptionManager only once subInfoList = mListener.getActiveSubscriptionsInfo(); Loading @@ -143,7 +188,7 @@ public class ActiveSubsciptionsListenerTest { mListener.clearCache(); } when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(null); mShadowSubscriptionManager.setActiveSubscriptionInfoList(null); // fetch twice and test if they generated access to SubscriptionManager only once subInfoList = mListener.getActiveSubscriptionsInfo(); Loading
tests/robotests/src/com/android/settings/network/GlobalSettingsChangeListenerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Looper; import android.provider.Settings; import androidx.lifecycle.Lifecycle; Loading Loading @@ -49,7 +50,8 @@ public class GlobalSettingsChangeListenerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mListener = spy(new GlobalSettingsChangeListener(mContext, SETTINGS_FIELD) { mListener = spy(new GlobalSettingsChangeListener(Looper.getMainLooper(), mContext, SETTINGS_FIELD) { public void onChanged(String field) {} }); Loading