Android 禁用三星“自动网络切换”以进行 WiFi 连接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20551658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Disabling Samsung "Auto Network Switch" for WiFi Connection
提问by Kevin Coppock
In our app, we handle initiating a WiFi connection to a device that broadcasts its own wireless access point (with no internet connection) for direct communication.
在我们的应用程序中,我们处理启动与设备的 WiFi 连接,该设备广播自己的无线接入点(没有互联网连接)以进行直接通信。
It works very well on all of our test devices; however, we're receiving reports from users that on certain Samsung devices (Galaxy S4, Galaxy Note 3) there is a setting under Wi-Fi Settings called "Auto Network Switch" that Samsung has added that looks for "unstable" networks, and will automatically disconnect and revert to mobile data. Unfortunately, since our device has no internet connection, Samsung reports it as un unstable network and immediately disconnects.
它适用于我们所有的测试设备;但是,我们收到用户的报告称,在某些三星设备(Galaxy S4、Galaxy Note 3)上,在 Wi-Fi 设置下有一个名为“自动网络切换”的设置,三星已添加该设置来查找“不稳定”网络,并且将自动断开连接并恢复到移动数据。不幸的是,由于我们的设备没有互联网连接,三星将其报告为网络不稳定并立即断开连接。
I don't have either of these devices available for testing on, so I'm curious if anyone else is aware of this issue or knows of a way to either programmatically disable this setting or work around it?
我没有可用于测试的这些设备中的任何一个,所以我很好奇是否有其他人知道这个问题或知道以编程方式禁用此设置或解决它的方法?
The code we use for the connection is:
我们用于连接的代码是:
/**
* Attempt to connect to an open wifi network with the given SSID
* @param ssid the SSID of the unsecured wireless network to connect to
*/
public static void connectToOpenNetwork (String ssid) {
WifiManager mgr = getManager();
WifiConfiguration configuration = getOpenWifiConfiguration(ssid);
mgr.addNetwork(configuration);
mgr.saveConfiguration();
int res = findOpenNetworkId(ssid);
if (res != INVALID_NETWORK_ID) {
mgr.enableNetwork(res, true);
mgr.reconnect();
} else {
Log.e(TAG, "Received request to connect to network " + ssid + " but the network was not found in the configurations.");
}
}
/**
* Get a WifiConfiguration object configured for an unsecured wireless network with the
* given SSID.
* @param ssid the SSID of the network to configure
* @return a WifiConfiguration object that can be passed to
* {@link WifiManager#addNetwork(android.net.wifi.WifiConfiguration)}
*/
private static WifiConfiguration getOpenWifiConfiguration (String ssid) {
WifiConfiguration config = new WifiConfiguration();
config.SSID = "\"" + ssid + "\"";
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
return config;
}
回答by Kevin Coppock
EDIT: So, further research for those interested, it seems to be a feature added in Samsung Touchwiz versions based on 4.3. Internally, the setting is named "wifi_watchdog_connectivity_check". I still use the below code to see if I'm able to check for surewhether or not the setting is enabled, but otherwise I'd have to assume it's on.
编辑:因此,对那些感兴趣的人进行进一步研究,这似乎是基于 4.3 的三星 Touchwiz 版本中添加的一项功能。在内部,该设置被命名为“wifi_watchdog_connectivity_check”。我仍然使用下面的代码来查看我是否能够确定该设置是否已启用,否则我必须假设它已启用。
So what I've discovered is that, after you attempt to make a connection, and the OS switches away from the network, the Wi-Fi configuration is in the "disabled" state. So after the problem occurs, you can detect for sure that it has happened by checking the state of the configuration from the WifiManager
.
所以我发现,在您尝试建立连接并且操作系统从网络切换后,Wi-Fi 配置处于“禁用”状态。因此,在问题发生后,您可以通过从WifiManager
.
WifiManager m = (WifiManger) getSystemService(Context.WIFI_SERVICE);
List<WifiConfiguration> networks = m.getConfiguredNetworks();
String mySsid = "My Network";
mySsid = "\"" + mySsid + "\"";
boolean isDisabled = false;
for (WifiConfiguration config : networks) {
if (mySsid.equals(config.SSID)) {
if (config.status = WifiConfiguration.Status.DISABLED) {
isDisabled = true;
break;
}
}
}
//If isDisabled is true, the network was disabled by the OS
You can then try to resolve the name of the setting from the system settings application:
然后,您可以尝试从系统设置应用程序解析设置的名称:
/** Gets the resources of another installed application */
private static Resources getExternalResources(Context ctx, String namespace) {
PackageManager pm = ctx.getPackageManager();
try {
return (pm == null) ? null : pm.getResourcesForApplication(namespace);
} catch (PackageManager.NameNotFoundException ex) {
return null;
}
}
/** Gets a resource ID from another installed application */
private static int getExternalIdentifier(Context ctx, String namespace,
String key, String type) {
Resources res = getExternalResources(ctx, namespace);
return (res == null) ? 0 : res.getIdentifier(key, type, namespace);
}
/** Gets a String resource from another installed application */
public static String getExternalString(Context ctx, String namespace,
String key, String defVal) {
int resId = getExternalIdentifier(ctx, namespace, key, "string");
if (resId != 0) {
Resources res = getExternalResources(ctx, namespace);
return res.getString(resId);
} else {
return defVal;
}
}
Then use it to get the string:
然后使用它来获取字符串:
String autoNetworkSwitch = getExternalString(this, "com.android.settings",
"wifi_watchdog_connectivity_check", "Unknown");
This will return a localized string for the current user's language, if the string exists.
如果字符串存在,这将返回当前用户语言的本地化字符串。
For anyone interested in the results of this, it turns out that this option is actually a stock Android setting, but it seems to be more aggressive on these Samsung devices. The setting is a hidden setting found in android.provider.Settings.java
:
对于对此结果感兴趣的任何人,事实证明此选项实际上是一个库存 Android 设置,但在这些三星设备上似乎更具侵略性。该设置是在android.provider.Settings.java
以下位置找到的隐藏设置:
/**
* Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
* the setting needs to be set to 0 to disable it.
* @hide
*/
public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
"wifi_watchdog_poor_network_test_enabled";
Which is in Settings$Secure
for API == 15 || API == 16
, or Settings$Global
for API >= 17
. This is not a setting that can be enabled or disabled by third party applications; however, it canbe detected and warned against. My solution is this:
哪个是Settings$Secure
for API == 15 || API == 16
,或Settings$Global
for API >= 17
。这不是第三方应用程序可以启用或禁用的设置;但是,它可以被检测到并发出警告。我的解决方案是这样的:
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
/**
* Checks whether the "Avoid poor networks" setting (named "Auto network switch" on
* some Samsung devices) is enabled, which can in some instances interfere with Wi-Fi.
*
* @return true if the "Avoid poor networks" or "Auto network switch" setting is enabled
*/
public static boolean isPoorNetworkAvoidanceEnabled (Context ctx) {
final int SETTING_UNKNOWN = -1;
final int SETTING_ENABLED = 1;
final String AVOID_POOR = "wifi_watchdog_poor_network_test_enabled";
final String WATCHDOG_CLASS = "android.net.wifi.WifiWatchdogStateMachine";
final String DEFAULT_ENABLED = "DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED";
final ContentResolver cr = ctx.getContentResolver();
int result;
if (SDK_INT >= JELLY_BEAN_MR1) {
//Setting was moved from Secure to Global as of JB MR1
result = Settings.Global.getInt(cr, AVOID_POOR, SETTING_UNKNOWN);
} else if (SDK_INT >= ICE_CREAM_SANDWICH_MR1) {
result = Settings.Secure.getInt(cr, AVOID_POOR, SETTING_UNKNOWN);
} else {
//Poor network avoidance not introduced until ICS MR1
//See android.provider.Settings.java
return false;
}
//Exit here if the setting value is known
if (result != SETTING_UNKNOWN) {
return (result == SETTING_ENABLED);
}
//Setting does not exist in database, so it has never been changed.
//It will be initialized to the default value.
if (SDK_INT >= JELLY_BEAN_MR1) {
//As of JB MR1, a constant was added to WifiWatchdogStateMachine to determine
//the default behavior of the Avoid Poor Networks setting.
try {
//In the case of any failures here, take the safe route and assume the
//setting is disabled to avoid disrupting the user with false information
Class wifiWatchdog = Class.forName(WATCHDOG_CLASS);
Field defValue = wifiWatchdog.getField(DEFAULT_ENABLED);
if (!defValue.isAccessible()) defValue.setAccessible(true);
return defValue.getBoolean(null);
} catch (IllegalAccessException ex) {
return false;
} catch (NoSuchFieldException ex) {
return false;
} catch (ClassNotFoundException ex) {
return false;
} catch (IllegalArgumentException ex) {
return false;
}
} else {
//Prior to JB MR1, the default for the Avoid Poor Networks setting was
//to enable it unless explicitly disabled
return true;
}
}
For good measure, you can then direct your users to the Advanced Wi-Fi Settings by means of Intent
:
为了更好地衡量,您可以通过以下方式将您的用户引导至高级 Wi-Fi 设置Intent
:
/**
* Ensure that an Activity is available to receive the given Intent
*/
public static boolean activityExists (Context ctx, Intent intent) {
final PackageManager mgr = ctx.getPackageManager();
final ResolveInfo info = mgr.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY);
return (info != null);
}
public static void showAdvancedWifiIfAvailable (Context ctx) {
final Intent i = new Intent(Settings.ACTION_WIFI_IP_SETTINGS);
if (activityExists(ctx, i)) {
ctx.startActivity(i);
}
}
Interesting trivia: This Intent
brings up the same Activity
as going to Settings > Wi-Fi > Advanced, but will show it with a different title (IP Settings, vs Advanced Wi-Fi).
有趣的琐事:这Intent
与Activity
转到“设置”>“Wi-Fi”>“高级”相同,但会以不同的标题显示(IP 设置与高级 Wi-Fi)。