Implement network avoid bad wifi from carrier config
Flag: android.net.platform.flags.avoid_bad_wifi_from_carrier_config Bug: 410976315 Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.connectivity.MultinetworkPolicyTrackerTest Change-Id: I89c579a8d5c7299303278280c463aa4c7c5bea99
This commit is contained in:
parent
ff278aa0d7
commit
0881fc0a7d
5 changed files with 493 additions and 47 deletions
|
|
@ -186,6 +186,7 @@ java_library {
|
|||
static_libs: [
|
||||
// Do not add libs here if they are already included
|
||||
// in framework-connectivity
|
||||
"android.net.platform.flags-aconfig-java-export",
|
||||
"androidx.annotation_annotation",
|
||||
"connectivity-net-module-utils-bpf",
|
||||
"connectivity_native_aidl_interface-lateststable-java",
|
||||
|
|
|
|||
|
|
@ -6711,7 +6711,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
pw.println("Config restrict: " + configRestrict);
|
||||
pw.println("Actively prefer bad wifi: " + activelyPreferBadWifi());
|
||||
|
||||
final String settingValue = mMultinetworkPolicyTracker.getAvoidBadWifiSetting();
|
||||
final String settingValue = mMultinetworkPolicyTracker.readAvoidBadWifiFromSettings();
|
||||
String description;
|
||||
// Can't use a switch statement because strings are legal case labels, but null is not.
|
||||
if ("0".equals(settingValue)) {
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION;
|
||||
import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
|
||||
import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
|
|
@ -29,19 +31,26 @@ import android.content.IntentFilter;
|
|||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.net.platform.flags.Flags;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.PersistableBundle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyCallback;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.connectivity.resources.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.modules.utils.BackgroundThread;
|
||||
import com.android.modules.utils.build.SdkLevel;
|
||||
import com.android.net.module.util.DeviceConfigUtils;
|
||||
import com.android.net.module.util.HandlerUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
|
@ -66,6 +75,23 @@ import java.util.concurrent.RejectedExecutionException;
|
|||
* @hide
|
||||
*/
|
||||
public class MultinetworkPolicyTracker {
|
||||
private class CarrierConfigChangeListener
|
||||
implements CarrierConfigManager.CarrierConfigChangeListener {
|
||||
/**
|
||||
* Must be called on the background thread.
|
||||
*/
|
||||
@Override
|
||||
public void onCarrierConfigChanged(
|
||||
int slotIndex, int subId, int carrierId, int specificCarrierId) {
|
||||
updateAvoidBadWifiFromCarrierConfigBackground(subId);
|
||||
}
|
||||
}
|
||||
|
||||
private enum AvoidBadWifiSource {
|
||||
FROM_RESOURCE,
|
||||
FROM_CARRIER_CONFIG,
|
||||
}
|
||||
|
||||
private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
|
||||
|
||||
// See Dependencies#getConfigActivelyPreferBadWifi
|
||||
|
|
@ -79,7 +105,16 @@ public class MultinetworkPolicyTracker {
|
|||
private final ContentResolver mResolver;
|
||||
private final SettingObserver mSettingObserver;
|
||||
private final BroadcastReceiver mBroadcastReceiver;
|
||||
private final boolean mAvoidBadWifiFromCarrierConfigFeature;
|
||||
private final boolean mHasTelephonySubscription;
|
||||
// This will be null if the FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG is off
|
||||
private final @Nullable CarrierConfigManager mCarrierConfigManager;
|
||||
private final @Nullable CarrierConfigChangeListener mCarrierConfigChangeListener;
|
||||
|
||||
// This will be accessed by background and handler thread
|
||||
@GuardedBy("mAvoidBadWifiCarrierConfigForSubIdMap")
|
||||
private final ArrayMap<Integer, Boolean> mAvoidBadWifiCarrierConfigForSubIdMap =
|
||||
new ArrayMap<>();
|
||||
private volatile boolean mAvoidBadWifi = true;
|
||||
private volatile int mMeteredMultipathPreference;
|
||||
private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
|
|
@ -90,6 +125,11 @@ public class MultinetworkPolicyTracker {
|
|||
*/
|
||||
@VisibleForTesting
|
||||
public static class Dependencies {
|
||||
private boolean isCarrierConfigValid(PersistableBundle config) {
|
||||
return config == null || config.isEmpty()
|
||||
|| !CarrierConfigManager.isConfigForIdentifiedCarrier(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DeviceConfigUtils#getDeviceConfigPropertyInt
|
||||
*/
|
||||
|
|
@ -119,6 +159,44 @@ public class MultinetworkPolicyTracker {
|
|||
return SubscriptionManager.getResourcesForSubId(
|
||||
resources.getResourcesContext(), activeSubId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean readAvoidBadWifiFromCarrierConfig(
|
||||
@NonNull final Context context, final int subId) {
|
||||
// Defaults to true to avoid potentially poor Wi-Fi and improve user experience.
|
||||
final boolean defaultConfig = true;
|
||||
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
PersistableBundle config = null;
|
||||
final CarrierConfigManager ccm = context.getSystemService(CarrierConfigManager.class);
|
||||
|
||||
try {
|
||||
config = ccm.getConfigForSubId(subId,
|
||||
CarrierConfigManager.KEY_AVOID_BAD_WIFI_BOOL
|
||||
);
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Failed to get config from carrier config", e);
|
||||
}
|
||||
|
||||
if (isCarrierConfigValid(config)) {
|
||||
// If the "avoid bad Wi-Fi" setting is not defined in the carrier-specific config,
|
||||
// retrieve the default configuration.
|
||||
config = CarrierConfigManager.getDefaultConfig();
|
||||
}
|
||||
|
||||
return config.getBoolean(CarrierConfigManager.KEY_AVOID_BAD_WIFI_BOOL, defaultConfig);
|
||||
}
|
||||
|
||||
protected boolean getAvoidBadWifiFromCarrierConfigFeature() {
|
||||
return Flags.avoidBadWifiFromCarrierConfig();
|
||||
}
|
||||
|
||||
protected Handler getBackgroundThreadHandler() {
|
||||
return BackgroundThread.getHandler();
|
||||
}
|
||||
|
||||
}
|
||||
private final Dependencies mDeps;
|
||||
|
||||
|
|
@ -164,7 +242,7 @@ public class MultinetworkPolicyTracker {
|
|||
protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
|
||||
implements TelephonyCallback.ActiveDataSubscriptionIdListener {
|
||||
@Override
|
||||
public void onActiveDataSubscriptionIdChanged(int subId) {
|
||||
public void onActiveDataSubscriptionIdChanged(final int subId) {
|
||||
mActiveSubId = subId;
|
||||
reevaluateInternal();
|
||||
}
|
||||
|
|
@ -193,8 +271,21 @@ public class MultinetworkPolicyTracker {
|
|||
}
|
||||
};
|
||||
|
||||
updateAvoidBadWifi();
|
||||
updateMeteredMultipathPreference();
|
||||
mHasTelephonySubscription =
|
||||
mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY_SUBSCRIPTION);
|
||||
mAvoidBadWifiFromCarrierConfigFeature = mDeps.getAvoidBadWifiFromCarrierConfigFeature();
|
||||
mCarrierConfigManager = mAvoidBadWifiFromCarrierConfigFeature
|
||||
? mContext.getSystemService(CarrierConfigManager.class)
|
||||
: null;
|
||||
|
||||
mCarrierConfigChangeListener = mAvoidBadWifiFromCarrierConfigFeature
|
||||
? new CarrierConfigChangeListener()
|
||||
: null;
|
||||
|
||||
if (!mAvoidBadWifiFromCarrierConfigFeature) {
|
||||
updateAvoidBadWifi();
|
||||
updateMeteredMultipathPreference();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
|
||||
|
|
@ -212,8 +303,20 @@ public class MultinetworkPolicyTracker {
|
|||
final Executor handlerExecutor = new HandlerExecutor(mHandler);
|
||||
mContext.getSystemService(TelephonyManager.class).registerTelephonyCallback(
|
||||
handlerExecutor, new ActiveDataSubscriptionIdListener());
|
||||
mDeps.addOnDevicePropertiesChangedListener(handlerExecutor,
|
||||
|
||||
if (mAvoidBadWifiFromCarrierConfigFeature) {
|
||||
mCarrierConfigManager.registerCarrierConfigChangeListener(
|
||||
BackgroundThread.getExecutor(), mCarrierConfigChangeListener
|
||||
);
|
||||
|
||||
// This ensures the latest carrier configuration is read.
|
||||
final int subId = mActiveSubId;
|
||||
mDeps.getBackgroundThreadHandler().post(() ->
|
||||
updateAvoidBadWifiFromCarrierConfigBackground(subId));
|
||||
} else {
|
||||
mDeps.addOnDevicePropertiesChangedListener(handlerExecutor,
|
||||
properties -> reevaluateInternal());
|
||||
}
|
||||
|
||||
reevaluate();
|
||||
}
|
||||
|
|
@ -222,6 +325,11 @@ public class MultinetworkPolicyTracker {
|
|||
mResolver.unregisterContentObserver(mSettingObserver);
|
||||
|
||||
mContext.unregisterReceiver(mBroadcastReceiver);
|
||||
if (mAvoidBadWifiFromCarrierConfigFeature) {
|
||||
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
|
||||
mCarrierConfigChangeListener
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getAvoidBadWifi() {
|
||||
|
|
@ -287,10 +395,13 @@ public class MultinetworkPolicyTracker {
|
|||
* Whether we should display a notification when wifi becomes unvalidated.
|
||||
*/
|
||||
public boolean shouldNotifyWifiUnvalidated() {
|
||||
return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
|
||||
return configRestrictsAvoidBadWifi() && readAvoidBadWifiFromSettings() == null;
|
||||
}
|
||||
|
||||
public String getAvoidBadWifiSetting() {
|
||||
/**
|
||||
* Retrieves the "avoid bad Wi-Fi" setting from the global settings.
|
||||
*/
|
||||
public String readAvoidBadWifiFromSettings() {
|
||||
return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
|
||||
}
|
||||
|
||||
|
|
@ -332,20 +443,119 @@ public class MultinetworkPolicyTracker {
|
|||
updateMeteredMultipathPreference();
|
||||
}
|
||||
|
||||
public boolean updateAvoidBadWifi() {
|
||||
final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
|
||||
final boolean prevAvoid = mAvoidBadWifi;
|
||||
mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
|
||||
/**
|
||||
* Reads the avoid bad Wi-Fi setting from the cache for a specific subscription ID.
|
||||
* If no value is found for the given subId, it defaults to true.
|
||||
*/
|
||||
private boolean readAvoidBadWifiFromCache(int subId) {
|
||||
synchronized (mAvoidBadWifiCarrierConfigForSubIdMap) {
|
||||
return mAvoidBadWifiCarrierConfigForSubIdMap.getOrDefault(subId, true);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean prevActive = mActivelyPreferBadWifi;
|
||||
final Boolean deviceConfigPreferBadWifi = deviceConfigActivelyPreferBadWifi();
|
||||
if (null == deviceConfigPreferBadWifi) {
|
||||
mActivelyPreferBadWifi = configActivelyPrefersBadWifi();
|
||||
} else {
|
||||
mActivelyPreferBadWifi = deviceConfigPreferBadWifi;
|
||||
/**
|
||||
* Updates the cached avoid bad Wi-Fi setting for a specific subscription ID.
|
||||
*/
|
||||
private void updateAvoidBadWifiCache(int subId, boolean enable) {
|
||||
synchronized (mAvoidBadWifiCarrierConfigForSubIdMap) {
|
||||
mAvoidBadWifiCarrierConfigForSubIdMap.put(subId, enable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the source of the avoid bad Wi-Fi setting.
|
||||
* It returns FROM_CARRIER_CONFIG if the carrier config feature is enabled,
|
||||
* otherwise it returns FROM_RESOURCE.
|
||||
*
|
||||
*/
|
||||
private AvoidBadWifiSource getAvoidBadWifiSource() {
|
||||
return mAvoidBadWifiFromCarrierConfigFeature
|
||||
? AvoidBadWifiSource.FROM_CARRIER_CONFIG
|
||||
: AvoidBadWifiSource.FROM_RESOURCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the local cache of the "avoid bad Wi-Fi" setting from the carrier config
|
||||
* for a specific subscription ID.
|
||||
* Must be called on the handler thread.
|
||||
*/
|
||||
private void updateAvoidBadWifiFromCarrierConfig(int subId, boolean enable) {
|
||||
updateAvoidBadWifiCache(subId, enable);
|
||||
|
||||
if (subId == mActiveSubId) {
|
||||
reevaluateInternal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the "avoid bad Wi-Fi" setting from the carrier configuration for the given subId,
|
||||
* and updates the local cache asynchronously on the handler thread.
|
||||
* Must be called on the background thread, because it makes an IPC to the phone process.
|
||||
* It must not be called on the CS handler thread.
|
||||
*/
|
||||
private void updateAvoidBadWifiFromCarrierConfigBackground(int subId) {
|
||||
HandlerUtils.ensureRunningOnHandlerThread(mDeps.getBackgroundThreadHandler());
|
||||
|
||||
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
return mAvoidBadWifi != prevAvoid || mActivelyPreferBadWifi != prevActive;
|
||||
// CarrierConfigManager#getConfigForSubId() is supported
|
||||
// only when system has FEATURE_TELEPHONY_SUBSCRIPTION
|
||||
final boolean config = !mHasTelephonySubscription
|
||||
|| mDeps.readAvoidBadWifiFromCarrierConfig(mContext, subId);
|
||||
mHandler.post(() -> updateAvoidBadWifiFromCarrierConfig(subId, config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the "avoid bad Wi-Fi" setting.
|
||||
* Depending on whether the carrier config feature is enabled, this method uses different logic:
|
||||
*
|
||||
* If carrier config feature is ON (Android U+ and 25Q4+):
|
||||
* - Forces "actively prefer bad Wi-Fi" to true.
|
||||
* - Checks a system setting first. If it is enabled, we should avoid bad Wi-Fi.
|
||||
* - If the system setting isn't available, it uses a cached value from the carrier config
|
||||
* for the current subId.
|
||||
*
|
||||
* If carrier config feature is OFF:
|
||||
* - "Avoid bad Wi-Fi" is true if the system setting is enabled OR
|
||||
* if the carrier doesn't restrict avoiding bad Wi-Fi.
|
||||
* - "Actively prefer bad Wi-Fi" is based on a device-specific setting,
|
||||
* falling back to the carrier config if the device setting isn't available.
|
||||
*
|
||||
* Returns true if either the "avoid bad Wi-Fi" or "actively prefer bad Wi-Fi" setting changed,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean updateAvoidBadWifi() {
|
||||
final boolean prevAvoid = mAvoidBadWifi;
|
||||
if (getAvoidBadWifiSource() == AvoidBadWifiSource.FROM_CARRIER_CONFIG) {
|
||||
// Force update activelyPreferBadWifi since it will always be true in Android U+,
|
||||
// and mAvoidBadWifiFromCarrierConfigFeature is a trunk stable flag
|
||||
// that only exists in 25Q4+
|
||||
mActivelyPreferBadWifi = true;
|
||||
|
||||
final String settingAvoidBadWifiStr = readAvoidBadWifiFromSettings();
|
||||
if (settingAvoidBadWifiStr != null) {
|
||||
mAvoidBadWifi = "1".equals(settingAvoidBadWifiStr);
|
||||
} else {
|
||||
// Retrieve the avoid bad Wi-Fi setting from the local cache to avoid potential
|
||||
// issues or blocking from the IPC call getAvoidBadWifiCarrierConfigForSubId().
|
||||
mAvoidBadWifi = readAvoidBadWifiFromCache(mActiveSubId);
|
||||
}
|
||||
return mAvoidBadWifi != prevAvoid;
|
||||
} else {
|
||||
final boolean settingAvoidBadWifi = "1".equals(readAvoidBadWifiFromSettings());
|
||||
mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
|
||||
final boolean prevActive = mActivelyPreferBadWifi;
|
||||
final Boolean deviceConfigPreferBadWifi = deviceConfigActivelyPreferBadWifi();
|
||||
if (null == deviceConfigPreferBadWifi) {
|
||||
mActivelyPreferBadWifi = configActivelyPrefersBadWifi();
|
||||
} else {
|
||||
mActivelyPreferBadWifi = deviceConfigPreferBadWifi;
|
||||
}
|
||||
|
||||
return mAvoidBadWifi != prevAvoid || mActivelyPreferBadWifi != prevActive;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,32 +17,40 @@
|
|||
package com.android.server.connectivity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
|
||||
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
|
||||
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
|
||||
import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI
|
||||
import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE
|
||||
import android.net.platform.flags.Flags.FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.test.TestLooper
|
||||
import android.provider.Settings
|
||||
import android.telephony.CarrierConfigManager
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
import android.test.mock.MockContentResolver
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.connectivity.resources.R
|
||||
import com.android.internal.os.BackgroundThread
|
||||
import com.android.internal.util.test.FakeSettingsProvider
|
||||
import com.android.modules.utils.build.SdkLevel
|
||||
import com.android.server.connectivity.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule
|
||||
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule.FeatureFlag
|
||||
import com.android.testutils.postAndWait
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
|
|
@ -66,56 +74,156 @@ const val HANDLER_TIMEOUT_MS = 400
|
|||
@SmallTest
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
class MultinetworkPolicyTrackerTest {
|
||||
// This wrapper class prevents JUnit from attempting to load unsupported system classes
|
||||
// that are present in the System Test (S/T) image, which would otherwise cause test failures.
|
||||
private class CarrierConfigChangeRunner(
|
||||
val csLooper: TestLooper,
|
||||
val bgHandler: Handler,
|
||||
val listener: CarrierConfigManager.CarrierConfigChangeListener
|
||||
) {
|
||||
|
||||
fun runCarrierConfigChangeOnBackgroundThread(subId: Int) {
|
||||
bgHandler.postAndWait {
|
||||
listener.onCarrierConfigChanged(
|
||||
0, /* logicalSlotIndex */
|
||||
subId, /* subscriptionId */
|
||||
0, /* carrierId */
|
||||
0 /* specificCarrierId */
|
||||
)
|
||||
}
|
||||
csLooper.dispatchNext()
|
||||
}
|
||||
}
|
||||
|
||||
private val featureFlags = HashSet<String>()
|
||||
|
||||
// This will set feature flags from @FeatureFlag annotations
|
||||
// into the map before setUp() runs.
|
||||
@get:Rule
|
||||
val setFeatureFlagsRule = SetFeatureFlagsRule(
|
||||
{ name, enabled ->
|
||||
if (enabled == true) featureFlags.add(name) else featureFlags.remove(name) },
|
||||
{ name -> featureFlags.contains(name) }
|
||||
)
|
||||
|
||||
@get:Rule
|
||||
val ignoreRule = DevSdkIgnoreRule()
|
||||
|
||||
private val resources = mock(Resources::class.java).also {
|
||||
doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
|
||||
doReturn(0).`when`(it).getInteger(R.integer.config_activelyPreferBadWifi)
|
||||
}
|
||||
|
||||
private val telephonyManager = mock(TelephonyManager::class.java)
|
||||
private val carrierConfigManager = mock(CarrierConfigManager::class.java)
|
||||
private val subscriptionManager = mock(SubscriptionManager::class.java).also {
|
||||
doReturn(null).`when`(it).getActiveSubscriptionInfo(anyInt())
|
||||
}
|
||||
private val packageManager = mock(PackageManager::class.java).also {
|
||||
doReturn(true).`when`(it)
|
||||
.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
|
||||
}
|
||||
private val resolver = MockContentResolver().apply {
|
||||
addProvider(Settings.AUTHORITY, FakeSettingsProvider()) }
|
||||
private val context = mock(Context::class.java).also {
|
||||
doReturn(Context.TELEPHONY_SERVICE).`when`(it)
|
||||
.getSystemServiceName(TelephonyManager::class.java)
|
||||
.getSystemServiceName(TelephonyManager::class.java)
|
||||
doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE)
|
||||
if (it.getSystemService(TelephonyManager::class.java) == null) {
|
||||
// Test is using mockito extended
|
||||
doCallRealMethod().`when`(it).getSystemService(TelephonyManager::class.java)
|
||||
}
|
||||
doReturn(subscriptionManager).`when`(it)
|
||||
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
|
||||
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
|
||||
doReturn(carrierConfigManager).`when`(it)
|
||||
.getSystemService(CarrierConfigManager::class.java)
|
||||
doReturn(resolver).`when`(it).contentResolver
|
||||
doReturn(resources).`when`(it).resources
|
||||
doReturn(it).`when`(it).createConfigurationContext(any())
|
||||
doReturn(packageManager).`when`(it).packageManager
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
|
||||
ConnectivityResources.setResourcesContextForTest(it)
|
||||
}
|
||||
private val csLooper = TestLooper()
|
||||
private val handler = Handler(csLooper.looper)
|
||||
private val trackerDependencies = MultinetworkPolicyTrackerTestDependencies(resources)
|
||||
private val tracker = MultinetworkPolicyTracker(context, handler,
|
||||
null /* avoidBadWifiCallback */, trackerDependencies)
|
||||
private val bgHandler = BackgroundThread.getHandler()
|
||||
private val trackerDependencies =
|
||||
MultinetworkPolicyTrackerTestDependencies(resources)
|
||||
|
||||
// this needs to be initialized in setUp() because
|
||||
// the featureFlag from the annotation is not ready here
|
||||
private lateinit var tracker: MultinetworkPolicyTracker
|
||||
private fun assertMultipathPreference(preference: Int) {
|
||||
Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
|
||||
preference.toString())
|
||||
Settings.Global.putString(
|
||||
resolver,
|
||||
NETWORK_METERED_MULTIPATH_PREFERENCE,
|
||||
preference.toString()
|
||||
)
|
||||
tracker.updateMeteredMultipathPreference()
|
||||
assertEquals(preference, tracker.meteredMultipathPreference)
|
||||
}
|
||||
|
||||
private fun runOnActiveDataSubscriptionIdChanged() {
|
||||
val testSubId = 1000
|
||||
val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */,
|
||||
"TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */,
|
||||
"123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */,
|
||||
""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */,
|
||||
"1"/* cardString */)
|
||||
doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId)
|
||||
|
||||
// Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in
|
||||
// MultinetworkPolicyTracker should be also updated after subId changed.
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
|
||||
Settings.Global.putString(
|
||||
resolver,
|
||||
NETWORK_METERED_MULTIPATH_PREFERENCE,
|
||||
MULTIPATH_PREFERENCE_PERFORMANCE.toString()
|
||||
)
|
||||
|
||||
assertTrue(tracker.avoidBadWifi)
|
||||
|
||||
val listenerCaptor = ArgumentCaptor.forClass(
|
||||
ActiveDataSubscriptionIdListener::class.java
|
||||
)
|
||||
verify(telephonyManager, times(1))
|
||||
.registerTelephonyCallback(any(), listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
csLooper.dispatchAll()
|
||||
|
||||
// Mock the carrier configuration to return false
|
||||
trackerDependencies.setAvoidBadWifiCarrierConfigForSubId(testSubId, false)
|
||||
listener.onActiveDataSubscriptionIdChanged(testSubId)
|
||||
|
||||
// Check if avoidBadWifi and meteredMultipathPreference values have been updated.
|
||||
assertFalse(tracker.avoidBadWifi)
|
||||
assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference)
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
trackerDependencies.setAvoidBadWifiFromCarrierConfigFeature(
|
||||
featureFlags.contains(FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG)
|
||||
)
|
||||
|
||||
trackerDependencies.setBackgroundThreadHandler(bgHandler)
|
||||
tracker = MultinetworkPolicyTracker(
|
||||
context,
|
||||
handler,
|
||||
null /* avoidBadWifiCallback */,
|
||||
trackerDependencies
|
||||
)
|
||||
tracker.start()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
ConnectivityResources.setResourcesContextForTest(null)
|
||||
trackerDependencies.resetAvoidBadWifiCarrierConfigForSubIdMap()
|
||||
}
|
||||
|
||||
@Test
|
||||
@FeatureFlag(name = FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG, false)
|
||||
fun testUpdateMeteredMultipathPreference() {
|
||||
assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER)
|
||||
assertMultipathPreference(MULTIPATH_PREFERENCE_RELIABILITY)
|
||||
|
|
@ -123,6 +231,7 @@ class MultinetworkPolicyTrackerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FeatureFlag(name = FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG, false)
|
||||
fun testUpdateAvoidBadWifi() {
|
||||
doReturn(0).`when`(resources).getInteger(R.integer.config_activelyPreferBadWifi)
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
|
||||
|
|
@ -165,32 +274,119 @@ class MultinetworkPolicyTrackerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun testOnActiveDataSubscriptionIdChanged() {
|
||||
val testSubId = 1000
|
||||
val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */,
|
||||
"TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */,
|
||||
"123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */,
|
||||
""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */,
|
||||
"1"/* cardString */)
|
||||
doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId)
|
||||
@FeatureFlag(name = FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG, false)
|
||||
fun testOnActiveDataSubscriptionIdChangedFromResources() {
|
||||
runOnActiveDataSubscriptionIdChanged()
|
||||
}
|
||||
|
||||
// Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in
|
||||
// MultinetworkPolicyTracker should be also updated after subId changed.
|
||||
@Test
|
||||
@FeatureFlag(name = FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG, true)
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.BAKLAVA)
|
||||
fun testOnActiveDataSubscriptionIdChangedFromCarrierConfig() {
|
||||
runOnActiveDataSubscriptionIdChanged()
|
||||
}
|
||||
|
||||
@Test
|
||||
@FeatureFlag(name = FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG, true)
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.BAKLAVA)
|
||||
fun testAvoidBadWifiWithGlobalSetting() {
|
||||
val activeSubId = 1000
|
||||
// Mock the initial global setting to false
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
|
||||
Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
|
||||
MULTIPATH_PREFERENCE_PERFORMANCE.toString())
|
||||
|
||||
assertTrue(tracker.avoidBadWifi)
|
||||
// Mock the initial carrier configuration to return true
|
||||
trackerDependencies.setAvoidBadWifiCarrierConfigForSubId(activeSubId, true)
|
||||
|
||||
val listenerCaptor = ArgumentCaptor.forClass(
|
||||
ActiveDataSubscriptionIdListener::class.java)
|
||||
ActiveDataSubscriptionIdListener::class.java
|
||||
)
|
||||
verify(telephonyManager, times(1))
|
||||
.registerTelephonyCallback(any(), listenerCaptor.capture())
|
||||
.registerTelephonyCallback(any(), listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
listener.onActiveDataSubscriptionIdChanged(testSubId)
|
||||
csLooper.dispatchAll()
|
||||
|
||||
// Check if avoidBadWifi and meteredMultipathPreference values have been updated.
|
||||
// Simulate a change in the active data subscription ID to activeSubId.
|
||||
listener.onActiveDataSubscriptionIdChanged(activeSubId)
|
||||
|
||||
// Assert that the tracker's avoidBadWifi flag is false based on global setting
|
||||
assertFalse(tracker.avoidBadWifi)
|
||||
assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference)
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
|
||||
// Mock the initial global setting to true
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
|
||||
// Mock the initial carrier configuration to return false
|
||||
trackerDependencies.setAvoidBadWifiCarrierConfigForSubId(activeSubId, false)
|
||||
|
||||
// Assert that the tracker's avoidBadWifi flag is true based on global setting
|
||||
assertTrue(tracker.updateAvoidBadWifi())
|
||||
assertTrue(tracker.avoidBadWifi)
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
}
|
||||
|
||||
@Test
|
||||
@FeatureFlag(name = FLAG_AVOID_BAD_WIFI_FROM_CARRIER_CONFIG, true)
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.BAKLAVA)
|
||||
fun testUpdateAvoidBadWifiOnCarrierConfigChange() {
|
||||
// Mock the initial global setting to null
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, null)
|
||||
|
||||
val activeSubId = 1000
|
||||
val listenerCaptor = ArgumentCaptor.forClass(
|
||||
ActiveDataSubscriptionIdListener::class.java
|
||||
)
|
||||
verify(telephonyManager, times(1))
|
||||
.registerTelephonyCallback(any(), listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
// Simulate a change in the active data subscription ID to activeSubId.
|
||||
listener.onActiveDataSubscriptionIdChanged(activeSubId)
|
||||
|
||||
val carrierConfiglistenCaptor = ArgumentCaptor.forClass(
|
||||
CarrierConfigManager.CarrierConfigChangeListener::class.java
|
||||
)
|
||||
|
||||
// Mock the initial carrier configuration to return false
|
||||
trackerDependencies.setAvoidBadWifiCarrierConfigForSubId(activeSubId, false)
|
||||
verify(carrierConfigManager, times(1))
|
||||
.registerCarrierConfigChangeListener(any(), carrierConfiglistenCaptor.capture())
|
||||
|
||||
val carrierConfiglistener = carrierConfiglistenCaptor.value
|
||||
// dispatch for the first carrier config initialization on the handler thread
|
||||
csLooper.dispatchAll()
|
||||
|
||||
val runner = CarrierConfigChangeRunner(csLooper, bgHandler, carrierConfiglistener)
|
||||
// Simulate a carrier configuration change for the initial activeSubId (1000),
|
||||
// with avoid bad Wi-Fi set to false.
|
||||
runner.runCarrierConfigChangeOnBackgroundThread(activeSubId)
|
||||
|
||||
// Assert that the tracker's avoidBadWifi flag is false after the carrier config change.
|
||||
assertFalse(tracker.avoidBadWifi)
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
|
||||
// Mock the carrier configuration to now return true for KEY_AVOID_BAD_WIFI_BOOL.
|
||||
trackerDependencies.setAvoidBadWifiCarrierConfigForSubId(activeSubId, true)
|
||||
// Simulate another carrier configuration change for the same activeSubId (1000),
|
||||
// now with avoid bad Wi-Fi set to true.
|
||||
runner.runCarrierConfigChangeOnBackgroundThread(activeSubId)
|
||||
|
||||
// Assert that the tracker's avoidBadWifi flag is now true.
|
||||
assertTrue(tracker.avoidBadWifi)
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
|
||||
val changedActiveSubId = 1001
|
||||
// Mock the carrier configuration to return false
|
||||
trackerDependencies.setAvoidBadWifiCarrierConfigForSubId(changedActiveSubId, false)
|
||||
// Simulate a carrier configuration change for a different subscription ID.
|
||||
runner.runCarrierConfigChangeOnBackgroundThread(changedActiveSubId)
|
||||
|
||||
// Assert that the tracker's avoidBadWifi flag remains true,
|
||||
// because the config change was for a non-active subscription.
|
||||
assertTrue(tracker.avoidBadWifi)
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
|
||||
// Simulate a change in the active data subscription ID to the new ID (1001).
|
||||
listener.onActiveDataSubscriptionIdChanged(changedActiveSubId)
|
||||
// Assert that the tracker's avoidBadWifi flag is now false,
|
||||
// reflecting the carrier config of the new active subscription.
|
||||
assertFalse(tracker.avoidBadWifi)
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,60 @@
|
|||
package com.android.server.connectivity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.os.Handler
|
||||
import android.provider.DeviceConfig
|
||||
import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
|
||||
import android.provider.DeviceConfig.OnPropertiesChangedListener
|
||||
import com.android.internal.annotations.GuardedBy
|
||||
import com.android.internal.os.BackgroundThread
|
||||
import com.android.server.connectivity.MultinetworkPolicyTracker.CONFIG_ACTIVELY_PREFER_BAD_WIFI
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
class MultinetworkPolicyTrackerTestDependencies(private val resources: Resources) :
|
||||
MultinetworkPolicyTracker.Dependencies() {
|
||||
class MultinetworkPolicyTrackerTestDependencies(
|
||||
private val resources: Resources
|
||||
) : MultinetworkPolicyTracker.Dependencies() {
|
||||
@GuardedBy("listeners")
|
||||
private var configActivelyPreferBadWifi = 0
|
||||
|
||||
// TODO : move this to an actual fake device config object
|
||||
@GuardedBy("listeners")
|
||||
private val listeners = mutableListOf<Pair<Executor, OnPropertiesChangedListener>>()
|
||||
|
||||
private var avoidBadWifiFromCarrierConfigFeature = false
|
||||
private val avoidBadWifiCarrierConfigForSubIdMap = HashMap<Int, Boolean>()
|
||||
private var backgroundThreadHandler = BackgroundThread.getHandler()
|
||||
|
||||
fun putConfigActivelyPreferBadWifi(value: Int) {
|
||||
synchronized(listeners) {
|
||||
if (value == configActivelyPreferBadWifi) return
|
||||
configActivelyPreferBadWifi = value
|
||||
val p = DeviceConfig.Properties(NAMESPACE_CONNECTIVITY,
|
||||
mapOf(CONFIG_ACTIVELY_PREFER_BAD_WIFI to value.toString()))
|
||||
val p = DeviceConfig.Properties(
|
||||
NAMESPACE_CONNECTIVITY,
|
||||
mapOf(CONFIG_ACTIVELY_PREFER_BAD_WIFI to value.toString())
|
||||
)
|
||||
listeners.forEach { (executor, listener) ->
|
||||
executor.execute { listener.onPropertiesChanged(p) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setAvoidBadWifiFromCarrierConfigFeature(value: Boolean) {
|
||||
avoidBadWifiFromCarrierConfigFeature = value
|
||||
}
|
||||
|
||||
fun setAvoidBadWifiCarrierConfigForSubId(subId: Int, value: Boolean) {
|
||||
avoidBadWifiCarrierConfigForSubIdMap[subId] = value
|
||||
}
|
||||
|
||||
fun resetAvoidBadWifiCarrierConfigForSubIdMap() {
|
||||
avoidBadWifiCarrierConfigForSubIdMap.clear()
|
||||
}
|
||||
|
||||
fun setBackgroundThreadHandler(handler: Handler) {
|
||||
backgroundThreadHandler = handler
|
||||
}
|
||||
|
||||
override fun getConfigActivelyPreferBadWifi(): Int {
|
||||
return synchronized(listeners) { configActivelyPreferBadWifi }
|
||||
}
|
||||
|
|
@ -42,5 +69,17 @@ class MultinetworkPolicyTrackerTestDependencies(private val resources: Resources
|
|||
}
|
||||
|
||||
override fun getResourcesForActiveSubId(res: ConnectivityResources, id: Int): Resources =
|
||||
resources
|
||||
resources
|
||||
|
||||
override fun readAvoidBadWifiFromCarrierConfig(
|
||||
context: Context,
|
||||
subId: Int
|
||||
): Boolean =
|
||||
avoidBadWifiCarrierConfigForSubIdMap.getOrDefault(subId, true)
|
||||
|
||||
public override fun getAvoidBadWifiFromCarrierConfigFeature(): Boolean =
|
||||
avoidBadWifiFromCarrierConfigFeature
|
||||
|
||||
public override fun getBackgroundThreadHandler(): Handler =
|
||||
backgroundThreadHandler
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue