diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java index ae31b5643a9..e9cdb46e6fd 100644 --- a/src/com/android/settings/network/SubscriptionsPreferenceController.java +++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java @@ -19,16 +19,22 @@ package com.android.settings.network; import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON; + import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.provider.Settings; +import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.util.ArraySet; +import androidx.annotation.VisibleForTesting; import androidx.collection.ArrayMap; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; @@ -40,9 +46,14 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.network.telephony.DataConnectivityListener; import com.android.settings.network.telephony.MobileNetworkActivity; +import com.android.settings.network.telephony.MobileNetworkUtils; +import com.android.settings.network.telephony.SignalStrengthListener; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.net.SignalStrengthUtil; +import java.util.Collections; import java.util.Map; +import java.util.Set; /** * This manages a set of Preferences it places into a PreferenceGroup owned by some parent @@ -51,7 +62,8 @@ import java.util.Map; */ public class SubscriptionsPreferenceController extends AbstractPreferenceController implements LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient, - MobileDataEnabledListener.Client, DataConnectivityListener.Client { + MobileDataEnabledListener.Client, DataConnectivityListener.Client, + SignalStrengthListener.Callback { private static final String TAG = "SubscriptionsPrefCntrlr"; private UpdateListener mUpdateListener; @@ -62,6 +74,8 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl private SubscriptionsChangeListener mSubscriptionsListener; private MobileDataEnabledListener mDataEnabledListener; private DataConnectivityListener mConnectivityListener; + private SignalStrengthListener mSignalStrengthListener; + // Map of subscription id to Preference private Map mSubscriptionPreferences; @@ -102,6 +116,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mSubscriptionsListener = new SubscriptionsChangeListener(context, this); mDataEnabledListener = new MobileDataEnabledListener(context, this); mConnectivityListener = new DataConnectivityListener(context, this); + mSignalStrengthListener = new SignalStrengthListener(context, this); lifecycle.addObserver(this); } @@ -110,6 +125,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mSubscriptionsListener.start(); mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId()); mConnectivityListener.start(); + mSignalStrengthListener.resume(); update(); } @@ -118,6 +134,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mSubscriptionsListener.stop(); mDataEnabledListener.stop(); mConnectivityListener.stop(); + mSignalStrengthListener.pause(); } @Override @@ -136,6 +153,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mPreferenceGroup.removePreference(pref); } mSubscriptionPreferences.clear(); + mSignalStrengthListener.updateSubscriptionIds(Collections.emptySet()); mUpdateListener.onChildrenUpdated(); return; } @@ -144,16 +162,20 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mSubscriptionPreferences = new ArrayMap<>(); int order = mStartOrder; + final Set activeSubIds = new ArraySet<>(); + final int dataDefaultSubId = SubscriptionManager.getDefaultDataSubscriptionId(); for (SubscriptionInfo info : SubscriptionUtil.getActiveSubscriptions(mManager)) { final int subId = info.getSubscriptionId(); + activeSubIds.add(subId); Preference pref = existingPrefs.remove(subId); if (pref == null) { pref = new Preference(mPreferenceGroup.getContext()); mPreferenceGroup.addPreference(pref); } pref.setTitle(info.getDisplayName()); - pref.setSummary(getSummary(subId)); - pref.setIcon(R.drawable.ic_network_cell); + final boolean isDefaultForData = (subId == dataDefaultSubId); + pref.setSummary(getSummary(subId, isDefaultForData)); + setIcon(pref, subId, isDefaultForData); pref.setOrder(order++); pref.setOnPreferenceClickListener(clickedPref -> { @@ -165,6 +187,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mSubscriptionPreferences.put(subId, pref); } + mSignalStrengthListener.updateSubscriptionIds(activeSubIds); // Remove any old preferences that no longer map to a subscription. for (Preference pref : existingPrefs.values()) { @@ -173,6 +196,32 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl mUpdateListener.onChildrenUpdated(); } + @VisibleForTesting + boolean shouldInflateSignalStrength(int subId) { + return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); + } + + @VisibleForTesting + void setIcon(Preference pref, int subId, boolean isDefaultForData) { + final TelephonyManager mgr = mContext.getSystemService( + TelephonyManager.class).createForSubscriptionId(subId); + final SignalStrength strength = mgr.getSignalStrength(); + int level = (strength == null) ? 0 : strength.getLevel(); + int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; + if (shouldInflateSignalStrength(subId)) { + level += 1; + numLevels += 1; + } + final boolean showCutOut = !isDefaultForData || !mgr.isDataEnabled(); + pref.setIcon(getIcon(level, numLevels, showCutOut)); + } + + @VisibleForTesting + Drawable getIcon(int level, int numLevels, boolean cutOut) { + return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels, + NO_CELL_DATA_TYPE_ICON, cutOut); + } + private boolean activeNetworkIsCellular() { final Network activeNetwork = mConnectivityManager.getActiveNetwork(); if (activeNetwork == null) { @@ -197,10 +246,9 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl * * If a subscription isn't the default for anything, we just say it is available. */ - protected String getSummary(int subId) { + protected String getSummary(int subId, boolean isDefaultForData) { final int callsDefaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId(); final int smsDefaultSubId = SubscriptionManager.getDefaultSmsSubscriptionId(); - final int dataDefaultSubId = SubscriptionManager.getDefaultDataSubscriptionId(); String line1 = null; if (subId == callsDefaultSubId && subId == smsDefaultSubId) { @@ -212,10 +260,10 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl } String line2 = null; - if (subId == dataDefaultSubId) { + if (isDefaultForData) { final TelephonyManager telMgrForSub = mContext.getSystemService( TelephonyManager.class).createForSubscriptionId(subId); - boolean dataEnabled = telMgrForSub.isDataEnabled(); + final boolean dataEnabled = telMgrForSub.isDataEnabled(); if (dataEnabled && activeNetworkIsCellular()) { line2 = mContext.getString(R.string.mobile_data_active); } else if (!dataEnabled) { @@ -277,4 +325,9 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl public void onDataConnectivityChange() { update(); } + + @Override + public void onSignalStrengthChanged() { + update(); + } } diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java index c0b45907097..0e5eaa89eda 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java +++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java @@ -24,6 +24,10 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.os.PersistableBundle; import android.os.SystemProperties; import android.provider.Settings; @@ -37,6 +41,7 @@ import android.telephony.euicc.EuiccManager; import android.telephony.ims.feature.ImsFeature; import android.text.TextUtils; import android.util.Log; +import android.view.Gravity; import androidx.annotation.VisibleForTesting; @@ -45,7 +50,10 @@ import com.android.ims.ImsManager; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; +import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.graph.SignalDrawable; import java.util.Arrays; import java.util.List; @@ -65,6 +73,10 @@ public class MobileNetworkUtils { private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE"; + // The following constants are used to draw signal icon. + public static final int NO_CELL_DATA_TYPE_ICON = 0; + public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT); + /** * Returns if DPC APNs are enforced. */ @@ -495,4 +507,32 @@ public class MobileNetworkUtils { return false; } + + public static Drawable getSignalStrengthIcon(Context context, int level, int numLevels, + int iconType, boolean cutOut) { + SignalDrawable signalDrawable = new SignalDrawable(context); + signalDrawable.setLevel( + SignalDrawable.getState(level, numLevels, cutOut)); + + // Make the network type drawable + Drawable networkDrawable = + iconType == NO_CELL_DATA_TYPE_ICON + ? EMPTY_DRAWABLE + : context + .getResources().getDrawable(iconType, context.getTheme()); + + // Overlay the two drawables + final Drawable[] layers = {networkDrawable, signalDrawable}; + final int iconSize = + context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size); + + LayerDrawable icons = new LayerDrawable(layers); + // Set the network type icon at the top left + icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT); + // Set the signal strength icon at the bottom right + icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT); + icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize); + icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal)); + return icons; + } } diff --git a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java index 2359399478e..77f40da7f17 100644 --- a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java +++ b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java @@ -16,22 +16,17 @@ package com.android.settings.network.telephony; +import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS; + import android.content.Context; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.telephony.CellInfo; import android.telephony.CellSignalStrength; -import android.telephony.SignalStrength; import android.util.Log; -import android.view.Gravity; import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settingslib.graph.SignalDrawable; import java.util.List; @@ -45,18 +40,12 @@ public class NetworkOperatorPreference extends Preference { private static final int LEVEL_NONE = -1; - // number of signal strength level - public static final int NUMBER_OF_LEVELS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; private CellInfo mCellInfo; private List mForbiddenPlmns; private int mLevel = LEVEL_NONE; private boolean mShow4GForLTE; private boolean mUseNewApi; - // The following constants are used to draw signal icon. - private static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT); - private static final int NO_CELL_DATA_CONNECTED_ICON = 0; - public NetworkOperatorPreference( CellInfo cellinfo, Context context, List forbiddenPlmns, boolean show4GForLTE) { super(context); @@ -113,39 +102,16 @@ public class NetworkOperatorPreference extends Preference { case CellInfo.TYPE_CDMA: return R.drawable.signal_strength_1x; default: - return 0; + return MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON; } } private void updateIcon(int level) { - if (!mUseNewApi || level < 0 || level >= NUMBER_OF_LEVELS) { + if (!mUseNewApi || level < 0 || level >= NUM_SIGNAL_STRENGTH_BINS) { return; } Context context = getContext(); - SignalDrawable signalDrawable = new SignalDrawable(getContext()); - signalDrawable.setLevel( - SignalDrawable.getState(level, NUMBER_OF_LEVELS, false /* cutOut */)); - - // Make the network type drawable - int iconType = getIconIdForCell(mCellInfo); - Drawable networkDrawable = - iconType == NO_CELL_DATA_CONNECTED_ICON - ? EMPTY_DRAWABLE - : getContext() - .getResources().getDrawable(iconType, getContext().getTheme()); - - // Overlay the two drawables - final Drawable[] layers = {networkDrawable, signalDrawable}; - final int iconSize = - context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size); - - LayerDrawable icons = new LayerDrawable(layers); - // Set the network type icon at the top left - icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT); - // Set the signal strength icon at the bottom right - icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT); - icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize); - icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal)); - setIcon(icons); + setIcon(MobileNetworkUtils.getSignalStrengthIcon(context, level, NUM_SIGNAL_STRENGTH_BINS, + getIconIdForCell(mCellInfo), false)); } } diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java index 98bfc0d72cd..62947d125d9 100644 --- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java +++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java @@ -29,6 +29,7 @@ import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; @@ -336,7 +337,7 @@ public class NetworkSelectSettings extends DashboardFragment { pref.setSummary(R.string.network_connected); // Update the signal strength icon, since the default signalStrength value would be // zero (it would be quite confusing why the connected network has no signal) - pref.setIcon(NetworkOperatorPreference.NUMBER_OF_LEVELS - 1); + pref.setIcon(SignalStrength.NUM_SIGNAL_STRENGTH_BINS - 1); mConnectedPreferenceCategory.addPreference(pref); } else { // Remove the connected network operators category diff --git a/src/com/android/settings/network/telephony/SignalStrengthListener.java b/src/com/android/settings/network/telephony/SignalStrengthListener.java new file mode 100644 index 00000000000..0e29a45fb5b --- /dev/null +++ b/src/com/android/settings/network/telephony/SignalStrengthListener.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings.network.telephony; + +import android.content.Context; +import android.telephony.PhoneStateListener; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.util.ArraySet; + +import com.google.common.collect.Sets; + +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** Helper class to manage listening to signal strength changes on a set of mobile network + * subscriptions */ +public class SignalStrengthListener { + + private TelephonyManager mBaseTelephonyManager; + private Callback mCallback; + private Map mListeners; + + public interface Callback { + void onSignalStrengthChanged(); + } + + public SignalStrengthListener(Context context, Callback callback) { + mBaseTelephonyManager = context.getSystemService(TelephonyManager.class); + mCallback = callback; + mListeners = new TreeMap<>(); + } + + /** Resumes listening for signal strength changes for the set of ids from the last call to + * {@link #updateSubscriptionIds(Set)} */ + public void resume() { + for (int subId : mListeners.keySet()) { + startListening(subId); + } + } + + /** Pauses listening for signal strength changes */ + public void pause() { + for (int subId : mListeners.keySet()) { + stopListening(subId); + } + } + + /** Updates the set of ids we want to be listening for, beginning to listen for any new ids and + * stopping listening for any ids not contained in the new set */ + public void updateSubscriptionIds(Set ids) { + Set currentIds = new ArraySet<>(mListeners.keySet()); + for (int idToRemove : Sets.difference(currentIds, ids)) { + stopListening(idToRemove); + mListeners.remove(idToRemove); + } + for (int idToAdd : Sets.difference(ids, currentIds)) { + PhoneStateListener listener = new PhoneStateListener() { + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + mCallback.onSignalStrengthChanged(); + } + }; + mListeners.put(idToAdd, listener); + startListening(idToAdd); + } + } + + private void startListening(int subId) { + TelephonyManager mgr = mBaseTelephonyManager.createForSubscriptionId(subId); + mgr.listen(mListeners.get(subId), PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); + } + + private void stopListening(int subId) { + TelephonyManager mgr = mBaseTelephonyManager.createForSubscriptionId(subId); + mgr.listen(mListeners.get(subId), PhoneStateListener.LISTEN_NONE); + } +} diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java index d6edcc72a87..51f8ec09fc9 100644 --- a/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java @@ -16,11 +16,17 @@ package com.android.settings.network; +import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS; +import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GOOD; +import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT; +import static android.telephony.SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; +import static android.telephony.SignalStrength.SIGNAL_STRENGTH_POOR; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -33,16 +39,20 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.provider.Settings; +import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.settings.R; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.graph.SignalDrawable; import org.junit.After; import org.junit.Before; @@ -84,6 +94,8 @@ public class SubscriptionsPreferenceControllerTest { private Network mActiveNetwork; @Mock private NetworkCapabilities mCapabilities; + @Mock + private Drawable mSignalStrengthIcon; private Context mContext; private LifecycleOwner mLifecycleOwner; @@ -109,8 +121,10 @@ public class SubscriptionsPreferenceControllerTest { mOnChildUpdatedCount = 0; mUpdateListener = () -> mOnChildUpdatedCount++; - mController = new SubscriptionsPreferenceController(mContext, mLifecycle, mUpdateListener, - KEY, 5); + mController = spy( + new SubscriptionsPreferenceController(mContext, mLifecycle, mUpdateListener, + KEY, 5)); + doReturn(mSignalStrengthIcon).when(mController).getIcon(anyInt(), anyInt(), anyBoolean()); } @After @@ -120,32 +134,25 @@ public class SubscriptionsPreferenceControllerTest { @Test public void isAvailable_oneSubscription_availableFalse() { - SubscriptionUtil.setActiveSubscriptionsForTesting( - Arrays.asList(mock(SubscriptionInfo.class))); + setupMockSubscriptions(1); assertThat(mController.isAvailable()).isFalse(); } @Test public void isAvailable_twoSubscriptions_availableTrue() { - SubscriptionUtil.setActiveSubscriptionsForTesting( - Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class))); + setupMockSubscriptions(2); assertThat(mController.isAvailable()).isTrue(); } @Test public void isAvailable_fiveSubscriptions_availableTrue() { - final ArrayList subs = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - subs.add(mock(SubscriptionInfo.class)); - } - SubscriptionUtil.setActiveSubscriptionsForTesting(subs); + setupMockSubscriptions(5); assertThat(mController.isAvailable()).isTrue(); } @Test public void isAvailable_airplaneModeOn_availableFalse() { - SubscriptionUtil.setActiveSubscriptionsForTesting( - Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class))); + setupMockSubscriptions(2); assertThat(mController.isAvailable()).isTrue(); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); assertThat(mController.isAvailable()).isFalse(); @@ -153,8 +160,7 @@ public class SubscriptionsPreferenceControllerTest { @Test public void onAirplaneModeChanged_airplaneModeTurnedOn_eventFired() { - SubscriptionUtil.setActiveSubscriptionsForTesting( - Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class))); + setupMockSubscriptions(2); mController.onResume(); mController.displayPreference(mScreen); assertThat(mController.isAvailable()).isTrue(); @@ -169,8 +175,7 @@ public class SubscriptionsPreferenceControllerTest { @Test public void onAirplaneModeChanged_airplaneModeTurnedOff_eventFired() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); - SubscriptionUtil.setActiveSubscriptionsForTesting( - Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class))); + setupMockSubscriptions(2); mController.onResume(); mController.displayPreference(mScreen); assertThat(mController.isAvailable()).isFalse(); @@ -184,15 +189,14 @@ public class SubscriptionsPreferenceControllerTest { @Test public void onSubscriptionsChanged_countBecameTwo_eventFired() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1)); + final List subs = setupMockSubscriptions(2); + SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 1)); mController.onResume(); mController.displayPreference(mScreen); assertThat(mController.isAvailable()).isFalse(); final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount; - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + SubscriptionUtil.setActiveSubscriptionsForTesting(subs); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isTrue(); assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1); @@ -200,18 +204,14 @@ public class SubscriptionsPreferenceControllerTest { @Test public void onSubscriptionsChanged_countBecameOne_eventFiredAndPrefsRemoved() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - when(sub1.getSubscriptionId()).thenReturn(1); - when(sub2.getSubscriptionId()).thenReturn(2); - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + final List subs = setupMockSubscriptions(2); mController.onResume(); mController.displayPreference(mScreen); assertThat(mController.isAvailable()).isTrue(); verify(mPreferenceCategory, times(2)).addPreference(any(Preference.class)); final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount; - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1)); + SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 1)); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isFalse(); assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1); @@ -219,21 +219,12 @@ public class SubscriptionsPreferenceControllerTest { verify(mPreferenceCategory, times(2)).removePreference(any(Preference.class)); } - @Test public void onSubscriptionsChanged_subscriptionReplaced_preferencesChanged() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub3 = mock(SubscriptionInfo.class); - when(sub1.getDisplayName()).thenReturn("sub1"); - when(sub2.getDisplayName()).thenReturn("sub2"); - when(sub3.getDisplayName()).thenReturn("sub3"); - when(sub1.getSubscriptionId()).thenReturn(1); - when(sub2.getSubscriptionId()).thenReturn(2); - when(sub3.getSubscriptionId()).thenReturn(3); + final List subs = setupMockSubscriptions(3); // Start out with only sub1 and sub2. - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 2)); mController.onResume(); mController.displayPreference(mScreen); final ArgumentCaptor captor = ArgumentCaptor.forClass(Preference.class); @@ -245,7 +236,7 @@ public class SubscriptionsPreferenceControllerTest { // Now replace sub2 with sub3, and make sure the old preference was removed and the new // preference was added. final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount; - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub3)); + SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(subs.get(0), subs.get(2))); mController.onSubscriptionsChanged(); assertThat(mController.isAvailable()).isTrue(); assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1); @@ -264,14 +255,8 @@ public class SubscriptionsPreferenceControllerTest { * @param subscriptionCount the number of subscriptions * @param selectedPrefIndex index of the subscription to click on */ - private void runPreferenceClickTest(int subscriptionCount, int selectedPrefIndex) { - final ArrayList subscriptions = new ArrayList<>(); - for (int i = 0; i < subscriptionCount; i++) { - final SubscriptionInfo sub = mock(SubscriptionInfo.class); - doReturn(i + 1).when(sub).getSubscriptionId(); - subscriptions.add(sub); - } - SubscriptionUtil.setActiveSubscriptionsForTesting(subscriptions); + private void runPreferenceClickTest(final int subscriptionCount, final int selectedPrefIndex) { + final List subs = setupMockSubscriptions(subscriptionCount); mController.displayPreference(mScreen); final ArgumentCaptor prefCaptor = ArgumentCaptor.forClass(Preference.class); verify(mPreferenceCategory, times(subscriptionCount)).addPreference(prefCaptor.capture()); @@ -286,7 +271,7 @@ public class SubscriptionsPreferenceControllerTest { final int subIdFromIntent = intent.getIntExtra(Settings.EXTRA_SUB_ID, INVALID_SUBSCRIPTION_ID); assertThat(subIdFromIntent).isEqualTo( - subscriptions.get(selectedPrefIndex).getSubscriptionId()); + subs.get(selectedPrefIndex).getSubscriptionId()); } @Test @@ -311,86 +296,248 @@ public class SubscriptionsPreferenceControllerTest { @Test public void getSummary_twoSubsOneDefaultForEverythingDataActive() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - when(sub1.getSubscriptionId()).thenReturn(11); - when(sub2.getSubscriptionId()).thenReturn(22); - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + setupMockSubscriptions(2); - ShadowSubscriptionManager.setDefaultDataSubscriptionId(11); ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11); ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11); when(mTelephonyManager.isDataEnabled()).thenReturn(true); when(mCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true); - assertThat(mController.getSummary(11)).isEqualTo( + assertThat(mController.getSummary(11, true)).isEqualTo( mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator() + mContext.getString(R.string.mobile_data_active)); - assertThat(mController.getSummary(22)).isEqualTo( + assertThat(mController.getSummary(22, false)).isEqualTo( mContext.getString(R.string.subscription_available)); } @Test public void getSummary_twoSubsOneDefaultForEverythingDataNotActive() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - when(sub1.getSubscriptionId()).thenReturn(11); - when(sub2.getSubscriptionId()).thenReturn(22); - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + setupMockSubscriptions(2, 1, true); - ShadowSubscriptionManager.setDefaultDataSubscriptionId(11); - ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11); - ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11); - when(mTelephonyManager.isDataEnabled()).thenReturn(true); + ShadowSubscriptionManager.setDefaultSmsSubscriptionId(1); + ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(1); - assertThat(mController.getSummary(11)).isEqualTo( + assertThat(mController.getSummary(1, true)).isEqualTo( mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator() + mContext.getString(R.string.default_for_mobile_data)); - assertThat(mController.getSummary(22)).isEqualTo( + assertThat(mController.getSummary(2, false)).isEqualTo( mContext.getString(R.string.subscription_available)); } @Test public void getSummary_twoSubsOneDefaultForEverythingDataDisabled() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - when(sub1.getSubscriptionId()).thenReturn(11); - when(sub2.getSubscriptionId()).thenReturn(22); - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + setupMockSubscriptions(2); - ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11); - ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11); - ShadowSubscriptionManager.setDefaultDataSubscriptionId(11); - when(mTelephonyManager.isDataEnabled()).thenReturn(false); + ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(1); + ShadowSubscriptionManager.setDefaultSmsSubscriptionId(1); - assertThat(mController.getSummary(11)).isEqualTo( + assertThat(mController.getSummary(1, true)).isEqualTo( mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator() + mContext.getString(R.string.mobile_data_off)); - assertThat(mController.getSummary(22)).isEqualTo( + assertThat(mController.getSummary(2, false)).isEqualTo( mContext.getString(R.string.subscription_available)); } @Test public void getSummary_twoSubsOneForCallsAndDataOneForSms() { - final SubscriptionInfo sub1 = mock(SubscriptionInfo.class); - final SubscriptionInfo sub2 = mock(SubscriptionInfo.class); - when(sub1.getSubscriptionId()).thenReturn(11); - when(sub2.getSubscriptionId()).thenReturn(22); - SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2)); + setupMockSubscriptions(2, 1, true); - ShadowSubscriptionManager.setDefaultDataSubscriptionId(11); - ShadowSubscriptionManager.setDefaultSmsSubscriptionId(22); - ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11); - when(mTelephonyManager.isDataEnabled()).thenReturn(true); + ShadowSubscriptionManager.setDefaultSmsSubscriptionId(2); + ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(1); - assertThat(mController.getSummary(11)).isEqualTo( + assertThat(mController.getSummary(1, true)).isEqualTo( mContext.getString(R.string.default_for_calls) + System.lineSeparator() + mContext.getString(R.string.default_for_mobile_data)); - assertThat(mController.getSummary(22)).isEqualTo( + assertThat(mController.getSummary(2, false)).isEqualTo( mContext.getString(R.string.default_for_sms)); } + + @Test + public void setIcon_nullStrength_noCrash() { + final List subs = setupMockSubscriptions(2); + setMockSubSignalStrength(subs, 0, -1); + final Preference pref = mock(Preference.class); + + mController.setIcon(pref, 1, true /* isDefaultForData */); + verify(mController).getIcon(eq(0), eq(NUM_SIGNAL_STRENGTH_BINS), eq(true)); + } + + @Test + public void setIcon_noSignal_correctLevels() { + final List subs = setupMockSubscriptions(2, 1, true); + setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + setMockSubDataEnabled(subs, 0, true); + final Preference pref = mock(Preference.class); + + mController.setIcon(pref, 1, true /* isDefaultForData */); + verify(mController).getIcon(eq(0), eq(NUM_SIGNAL_STRENGTH_BINS), eq(false)); + + mController.setIcon(pref, 2, false /* isDefaultForData */); + verify(mController).getIcon(eq(0), eq(NUM_SIGNAL_STRENGTH_BINS), eq(true)); + } + + @Test + public void setIcon_noSignal_withInflation_correctLevels() { + final List subs = setupMockSubscriptions(2, 1, true); + setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + final Preference pref = mock(Preference.class); + doReturn(true).when(mController).shouldInflateSignalStrength(anyInt()); + + mController.setIcon(pref, 1, true /* isDefaultForData */); + verify(mController).getIcon(eq(1), eq(NUM_SIGNAL_STRENGTH_BINS + 1), eq(false)); + + mController.setIcon(pref, 2, false /* isDefaultForData */); + verify(mController).getIcon(eq(1), eq(NUM_SIGNAL_STRENGTH_BINS + 1), eq(true)); + } + + @Test + public void setIcon_greatSignal_correctLevels() { + final List subs = setupMockSubscriptions(2, 1, true); + setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_GREAT); + setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GREAT); + final Preference pref = mock(Preference.class); + + mController.setIcon(pref, 1, true /* isDefaultForData */); + verify(mController).getIcon(eq(4), eq(NUM_SIGNAL_STRENGTH_BINS), eq(false)); + + mController.setIcon(pref, 2, false /* isDefaultForData */); + verify(mController).getIcon(eq(4), eq(NUM_SIGNAL_STRENGTH_BINS), eq(true)); + } + + @Test + public void onSignalStrengthChanged_subTwoGoesFromGoodToGreat_correctLevels() { + final List subs = setupMockSubscriptions(2); + setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_POOR); + setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GOOD); + + mController.onResume(); + mController.displayPreference(mScreen); + + // Now change the signal strength for the 2nd subscription from Good to Great + setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GREAT); + mController.onSignalStrengthChanged(); + + final ArgumentCaptor level = ArgumentCaptor.forClass(Integer.class); + verify(mController, times(4)).getIcon(level.capture(), eq(NUM_SIGNAL_STRENGTH_BINS), + eq(true)); + assertThat(level.getAllValues().get(0)).isEqualTo(1); + assertThat(level.getAllValues().get(1)).isEqualTo(3); // sub2, first time + assertThat(level.getAllValues().get(2)).isEqualTo(1); + assertThat(level.getAllValues().get(3)).isEqualTo(4); // sub2, after change + } + + @Test + public void displayPreference_mobileDataOff_bothSubsHaveCutOut() { + setupMockSubscriptions(2, 1, false); + + mController.onResume(); + mController.displayPreference(mScreen); + + verify(mController, times(2)).getIcon(eq(SIGNAL_STRENGTH_GOOD), + eq(NUM_SIGNAL_STRENGTH_BINS), eq(true)); + } + + @Test + public void displayPreference_mobileDataOn_onlyNonDefaultSubHasCutOut() { + final List subs = setupMockSubscriptions(2, 1, true); + setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_POOR); + + mController.onResume(); + mController.displayPreference(mScreen); + + verify(mController).getIcon(eq(SIGNAL_STRENGTH_GOOD), eq(NUM_SIGNAL_STRENGTH_BINS), + eq(false)); + verify(mController).getIcon(eq(SIGNAL_STRENGTH_POOR), eq(NUM_SIGNAL_STRENGTH_BINS), + eq(true)); + } + + + @Test + public void onMobileDataEnabledChange_mobileDataTurnedOff_bothSubsHaveCutOut() { + final List subs = setupMockSubscriptions(2, 1, true); + + mController.onResume(); + mController.displayPreference(mScreen); + + setMockSubDataEnabled(subs, 0, false); + mController.onMobileDataEnabledChange(); + + final ArgumentCaptor cutOutCaptor = ArgumentCaptor.forClass(Boolean.class); + verify(mController, times(4)).getIcon(eq(SIGNAL_STRENGTH_GOOD), + eq(NUM_SIGNAL_STRENGTH_BINS), cutOutCaptor.capture()); + assertThat(cutOutCaptor.getAllValues().get(0)).isEqualTo(false); // sub1, first time + assertThat(cutOutCaptor.getAllValues().get(1)).isEqualTo(true); + assertThat(cutOutCaptor.getAllValues().get(2)).isEqualTo(true); // sub1, second time + assertThat(cutOutCaptor.getAllValues().get(3)).isEqualTo(true); + } + + private List setupMockSubscriptions(int count) { + return setupMockSubscriptions(count, 0, true); + } + + /** Helper method to setup several mock active subscriptions. The generated subscription id's + * start at 1. + * + * @param count How many subscriptions to create + * @param defaultDataSubId The subscription id of the default data subscription - pass + * INVALID_SUBSCRIPTION_ID if there should not be one + * @param mobileDataEnabled Whether mobile data should be considered enabled for the default + * data subscription + */ + private List setupMockSubscriptions(int count, int defaultDataSubId, + boolean mobileDataEnabled) { + if (defaultDataSubId != INVALID_SUBSCRIPTION_ID) { + ShadowSubscriptionManager.setDefaultDataSubscriptionId(defaultDataSubId); + } + final ArrayList infos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + final int subscriptionId = i + 1; + final SubscriptionInfo info = mock(SubscriptionInfo.class); + final TelephonyManager mgrForSub = mock(TelephonyManager.class); + final SignalStrength signalStrength = mock(SignalStrength.class); + + if (subscriptionId == defaultDataSubId) { + when(mgrForSub.isDataEnabled()).thenReturn(mobileDataEnabled); + } + when(info.getSubscriptionId()).thenReturn(i + 1); + when(info.getDisplayName()).thenReturn("sub" + (i + 1)); + doReturn(mgrForSub).when(mTelephonyManager).createForSubscriptionId(eq(subscriptionId)); + when(mgrForSub.getSignalStrength()).thenReturn(signalStrength); + when(signalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GOOD); + + infos.add(info); + } + SubscriptionUtil.setActiveSubscriptionsForTesting(infos); + return infos; + } + + /** + * Helper method to set the signal strength returned for a mock subscription + * @param subs The list of subscriptions + * @param index The index in of the subscription in |subs| to change + * @param level The signal strength level to return for the subscription. Pass -1 to force + * return of a null SignalStrength object for the subscription. + */ + private void setMockSubSignalStrength(List subs, int index, int level) { + final TelephonyManager mgrForSub = + mTelephonyManager.createForSubscriptionId(subs.get(index).getSubscriptionId()); + if (level == -1) { + when(mgrForSub.getSignalStrength()).thenReturn(null); + } else { + final SignalStrength signalStrength = mgrForSub.getSignalStrength(); + when(signalStrength.getLevel()).thenReturn(level); + } + } + + private void setMockSubDataEnabled(List subs, int index, boolean enabled) { + final TelephonyManager mgrForSub = + mTelephonyManager.createForSubscriptionId(subs.get(index).getSubscriptionId()); + when(mgrForSub.isDataEnabled()).thenReturn(enabled); + } } diff --git a/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java b/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java new file mode 100644 index 00000000000..406f3604faa --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings.network.telephony; + +import static android.telephony.PhoneStateListener.LISTEN_NONE; +import static android.telephony.PhoneStateListener.LISTEN_SIGNAL_STRENGTHS; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +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 android.content.Context; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.collections.Sets; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class SignalStrengthListenerTest { + private static final int SUB_ID_1 = 111; + private static final int SUB_ID_2 = 222; + private static final int SUB_ID_3 = 333; + + @Mock + private SignalStrengthListener.Callback mCallback; + @Mock + private TelephonyManager mBaseManager; + @Mock + private TelephonyManager mManager1; + @Mock + private TelephonyManager mManager2; + @Mock + private TelephonyManager mManager3; + + private Context mContext; + private SignalStrengthListener mListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mBaseManager); + when(mBaseManager.createForSubscriptionId(SUB_ID_1)).thenReturn(mManager1); + when(mBaseManager.createForSubscriptionId(SUB_ID_2)).thenReturn(mManager2); + when(mBaseManager.createForSubscriptionId(SUB_ID_3)).thenReturn(mManager3); + mListener = new SignalStrengthListener(mContext, mCallback); + } + + @Test + public void resume_noIds_noCrash() { + mListener.resume(); + } + + @Test + public void pause_noIds_noCrash() { + mListener.resume(); + } + + @Test + public void updateSubscriptionIds_beforeResume_startedListening() { + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2)); + ArgumentCaptor captor1 = ArgumentCaptor.forClass( + PhoneStateListener.class); + ArgumentCaptor captor2 = ArgumentCaptor.forClass( + PhoneStateListener.class); + verify(mManager1).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager2).listen(captor2.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager3, never()).listen(any(), anyInt()); + assertThat(captor1.getValue()).isNotNull(); + assertThat(captor2.getValue()).isNotNull(); + + // Make sure the two listeners are separate objects. + assertThat(captor1.getValue() != captor2.getValue()).isTrue(); + } + + @Test + public void updateSubscriptionIds_twoCalls_oneIdAdded() { + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2)); + verify(mManager1).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager2).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS)); + + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2, SUB_ID_3)); + verify(mManager1, never()).listen(any(PhoneStateListener.class), eq(LISTEN_NONE)); + verify(mManager2, never()).listen(any(PhoneStateListener.class), eq(LISTEN_NONE)); + verify(mManager3).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS)); + } + + @Test + public void updateSubscriptionIds_twoCalls_oneIdRemoved() { + ArgumentCaptor captor1 = ArgumentCaptor.forClass( + PhoneStateListener.class); + + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2)); + verify(mManager1).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager2).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS)); + + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_2)); + verify(mManager1).listen(captor1.capture(), eq(LISTEN_NONE)); + verify(mManager2, never()).listen(any(PhoneStateListener.class), eq(LISTEN_NONE)); + // Make sure the correct listener was removed. + assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(1)).isTrue(); + } + + @Test + public void updateSubscriptionIds_twoCalls_twoIdsRemovedOneAdded() { + ArgumentCaptor captor1 = ArgumentCaptor.forClass( + PhoneStateListener.class); + ArgumentCaptor captor2 = ArgumentCaptor.forClass( + PhoneStateListener.class); + + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2)); + verify(mManager1).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager2).listen(captor2.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_3)); + verify(mManager1).listen(captor1.capture(), eq(LISTEN_NONE)); + verify(mManager2).listen(captor2.capture(), eq(LISTEN_NONE)); + verify(mManager3).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS)); + // Make sure the correct listeners were removed. + assertThat(captor1.getValue() != captor2.getValue()).isTrue(); + assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(1)).isTrue(); + assertThat(captor2.getAllValues().get(0) == captor2.getAllValues().get(1)).isTrue(); + } + + @Test + public void updateSubscriptionIds_thenPauseResume_correctlyStartsAndStops() { + mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2)); + mListener.pause(); + mListener.resume(); + + ArgumentCaptor captor1 = ArgumentCaptor.forClass( + PhoneStateListener.class); + ArgumentCaptor captor2 = ArgumentCaptor.forClass( + PhoneStateListener.class); + verify(mManager1, times(2)).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager1).listen(captor1.capture(), eq(LISTEN_NONE)); + + verify(mManager2, times(2)).listen(captor2.capture(), eq(LISTEN_SIGNAL_STRENGTHS)); + verify(mManager2).listen(captor2.capture(), eq(LISTEN_NONE)); + + assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(1)).isTrue(); + assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(2)).isTrue(); + + assertThat(captor2.getAllValues().get(0) == captor2.getAllValues().get(1)).isTrue(); + assertThat(captor2.getAllValues().get(0) == captor2.getAllValues().get(2)).isTrue(); + } +}