Merge "Developer settings: Disable wifi scan throttling" into qt-dev
diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index bf22b88..04c8f8d 100644
--- a/res/layout/master_clear.xml
+++ b/res/layout/master_clear.xml
@@ -21,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:theme="@style/SudThemeGlifV3.DayNight"
+ android:theme="@style/GlifV3Theme.Footer"
android:icon="@drawable/ic_delete_accent"
app:sucHeaderText="@string/master_clear_title">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 171fe42..2e505ab 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7889,8 +7889,8 @@
<!-- Configure Notifications: section header for prioritizer settings [CHAR LIMIT=80] -->
<string name="smart_notifications_title">Adaptive notifications</string>
- <!-- Configure Notifications: setting title [CHAR LIMIT=80] -->
- <string name="asst_capability_prioritizer_title">Automatic Prioritization</string>
+ <!-- Configure Notifications: setting title [CHAR LIMIT=80 BACKUP_MESSAGE_ID=6691908606916292167] -->
+ <string name="asst_capability_prioritizer_title">Adaptive notification priority</string>
<!-- Configure Notifications: setting summary [CHAR LIMIT=200] -->
<string name="asst_capability_prioritizer_summary">Automatically set lower priority notifications to Gentle</string>
@@ -10197,6 +10197,15 @@
<!-- Search keywords for System Navigation settings. [CHAR_LIMIT=NONE]-->
<string name="keywords_system_navigation">system navigation, 2 button navigation, 3 button navigation, gesture navigation</string>
+ <!-- Message for the alert dialog which says that the current default home app does not support gesture navigation. [CHAR LIMIT=NONE] -->
+ <string name="gesture_not_supported_dialog_message">Not supported by your default home app, <xliff:g id="default_home_app" example="Pixel Launcher">%s</xliff:g></string>
+
+ <!-- Positive button for the alert dialog when gesture nav not supported by launcher [CHAR LIMIT=40] -->
+ <string name="gesture_not_supported_positive_button">Switch default home app</string>
+
+ <!-- Content description for the Information icon [CHAR LIMIT=30] -->
+ <string name="information_label">Information</string>
+
<!-- Preference and settings suggestion title text for ambient display double tap (phone) [CHAR LIMIT=60]-->
<string name="ambient_display_title" product="default">Double-tap to check phone</string>
<!-- Preference and settings suggestion title text for ambient display double tap (tablet) [CHAR LIMIT=60]-->
@@ -11207,11 +11216,11 @@
<string name="forget_passpoint_dialog_message">You may lose access to any remaining time or data. Check with your provider before removing.</string>
<!-- Keywords for Content Capture feature [CHAR_LIMIT=none] -->
- <string name="keywords_content_capture">content capture, app content</string>
+ <string name="keywords_content_capture">content capture, app content, app data</string>
<!-- Title of the 'Content Capture' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=none]-->
- <string name="content_capture">App content</string>
+ <string name="content_capture">Personalize using app data</string>
<!-- Description of the 'Content Capture' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=NONE]-->
- <string name="content_capture_summary">Allow apps to send content to the Android system</string>
+ <string name="content_capture_summary">Allow apps to send content to the Android system. Tap to learn more.</string>
<!-- Title for the button to initiate a heap dump for the system server. [CHAR LIMIT=NONE] -->
<string name="capture_system_heap_dump_title">Capture system heap dump</string>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 8316971..3eda345 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -126,6 +126,8 @@
<style name="Theme.AlertDialog" parent="Theme.AlertDialog.Base">
<item name="android:windowSoftInputMode">adjustResize</item>
+ <item name="android:clipToPadding">true</item>
+ <item name="android:clipChildren">true</item>
<!-- Redefine the ActionBar style for contentInsetStart -->
<item name="android:actionBarStyle">@style/Widget.ActionBar</item>
diff --git a/res/values/themes_suw.xml b/res/values/themes_suw.xml
index add2fed..f7f1d16 100644
--- a/res/values/themes_suw.xml
+++ b/res/values/themes_suw.xml
@@ -112,6 +112,11 @@
<item name="*android:lockPatternStyle">@style/LockPatternStyle.Setup</item>
</style>
+ <style name="GlifV3Theme.Footer" parent="@style/SudThemeGlifV3.DayNight">
+ <item name="android:clipChildren">true</item>
+ <item name="android:clipToPadding">true</item>
+ </style>
+
<style name="GlifV3Theme.Light" parent="SudThemeGlifV3.Light">
<!-- For all AndroidX Alert Dialogs -->
<item name="alertDialogTheme">@style/GlifV2ThemeAlertDialog.Light</item>
@@ -209,4 +214,4 @@
<item name="colorAccent">@*android:color/accent_device_default_light</item>
<item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 573bc94..25982fe8 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -527,6 +527,11 @@
android:title="@string/show_notification_channel_warnings"
android:summary="@string/show_notification_channel_warnings_summary" />
+ <SwitchPreference
+ android:key="asst_capability_prioritizer"
+ android:title="@string/asst_capability_prioritizer_title"
+ settings:controller="com.android.settings.notification.AssistantCapabilityPreferenceController" />
+
<Preference
android:key="inactive_apps"
android:title="@string/inactive_apps_title"
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
index ae31b56..e9cdb46 100644
--- a/src/com/android/settings/network/SubscriptionsPreferenceController.java
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -19,16 +19,22 @@
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 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 @@
*/
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 @@
private SubscriptionsChangeListener mSubscriptionsListener;
private MobileDataEnabledListener mDataEnabledListener;
private DataConnectivityListener mConnectivityListener;
+ private SignalStrengthListener mSignalStrengthListener;
+
// Map of subscription id to Preference
private Map<Integer, Preference> mSubscriptionPreferences;
@@ -102,6 +116,7 @@
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 @@
mSubscriptionsListener.start();
mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
mConnectivityListener.start();
+ mSignalStrengthListener.resume();
update();
}
@@ -118,6 +134,7 @@
mSubscriptionsListener.stop();
mDataEnabledListener.stop();
mConnectivityListener.stop();
+ mSignalStrengthListener.pause();
}
@Override
@@ -136,6 +153,7 @@
mPreferenceGroup.removePreference(pref);
}
mSubscriptionPreferences.clear();
+ mSignalStrengthListener.updateSubscriptionIds(Collections.emptySet());
mUpdateListener.onChildrenUpdated();
return;
}
@@ -144,16 +162,20 @@
mSubscriptionPreferences = new ArrayMap<>();
int order = mStartOrder;
+ final Set<Integer> 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 @@
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 @@
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 @@
*
* 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 @@
}
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 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 c0b4590..0e5eaa8 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.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.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.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 @@
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 @@
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 2359399..77f40da 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 @@
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<String> 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<String> forbiddenPlmns, boolean show4GForLTE) {
super(context);
@@ -113,39 +102,16 @@
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 98bfc0d..62947d1 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.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 @@
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 0000000..0e29a45
--- /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<Integer, PhoneStateListener> 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<Integer> ids) {
+ Set<Integer> 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 d6edcc7..51f8ec0 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 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 @@
private Network mActiveNetwork;
@Mock
private NetworkCapabilities mCapabilities;
+ @Mock
+ private Drawable mSignalStrengthIcon;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
@@ -109,8 +121,10 @@
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 @@
@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<SubscriptionInfo> 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 @@
@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 @@
@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 @@
@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<SubscriptionInfo> 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 @@
@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<SubscriptionInfo> 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 @@
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<SubscriptionInfo> 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<Preference> captor = ArgumentCaptor.forClass(Preference.class);
@@ -245,7 +236,7 @@
// 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 @@
* @param subscriptionCount the number of subscriptions
* @param selectedPrefIndex index of the subscription to click on
*/
- private void runPreferenceClickTest(int subscriptionCount, int selectedPrefIndex) {
- final ArrayList<SubscriptionInfo> 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<SubscriptionInfo> subs = setupMockSubscriptions(subscriptionCount);
mController.displayPreference(mScreen);
final ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
verify(mPreferenceCategory, times(subscriptionCount)).addPreference(prefCaptor.capture());
@@ -286,7 +271,7 @@
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 @@
@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<SubscriptionInfo> 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<SubscriptionInfo> 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<SubscriptionInfo> 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<SubscriptionInfo> 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<SubscriptionInfo> 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<Integer> 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<SubscriptionInfo> 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<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
+
+ mController.onResume();
+ mController.displayPreference(mScreen);
+
+ setMockSubDataEnabled(subs, 0, false);
+ mController.onMobileDataEnabledChange();
+
+ final ArgumentCaptor<Boolean> 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<SubscriptionInfo> 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<SubscriptionInfo> setupMockSubscriptions(int count, int defaultDataSubId,
+ boolean mobileDataEnabled) {
+ if (defaultDataSubId != INVALID_SUBSCRIPTION_ID) {
+ ShadowSubscriptionManager.setDefaultDataSubscriptionId(defaultDataSubId);
+ }
+ final ArrayList<SubscriptionInfo> 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<SubscriptionInfo> 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<SubscriptionInfo> 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 0000000..406f360
--- /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<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
+ PhoneStateListener.class);
+ ArgumentCaptor<PhoneStateListener> 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<PhoneStateListener> 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<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
+ PhoneStateListener.class);
+ ArgumentCaptor<PhoneStateListener> 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<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
+ PhoneStateListener.class);
+ ArgumentCaptor<PhoneStateListener> 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();
+ }
+}